基于React.js的Web应用程序实现,完整示例
介绍
每个开发人员都可以在网上搜索并找到基于React
与Web应用程序相关的内容,并查看一些实现示例。 一些概念,例如:
- 服务器端渲染
- 使用React Router
- 使用CSS模块
- 使用PostCSS
- Webpack配置
- 表达
- 用于分隔开发和生产环境的配置
- 适用于大规模应用程序部署的强大工具
- 应用扩展
- 渐进式Web应用程序
- …
它们太多且非常复杂,以至于有时,大量的这些技术导致无法正确实施它们。
有时,我们会看到Pinterest , Instagram或Facebook之类的大公司的网站 ,这个问题向我们提出, 他们如何行动以及如何使用所有技术而不与大量开发人员发生冲突 。
在本文中,我想向您展示一个良好平台,完整且能够大规模转换的直观构想或示例。 我会逐步解释。
最后,您将拥有一个完整的存储库,可以构建一个包含趋势技术和技术的良好react
应用程序。 当然,您的导出在该领域看起来像是很好的例子,但这并不意味着您不能使其比现在更好。
TL; DR! :如果您无聊阅读所有文章,并且想立即查看结果,请参阅并使用此存储库 。
我希望,如果您有想法或愿景可以改善它,请在Github上进行分叉并在PR上进行。 我和安德鲁对此表示完全欢迎。
我们想要建立的
如果您在互联网上寻找React App,您会发现许多实现都具有SSR
, React Helmet
和许多其他东西。 但其中许多是根据开发目标而非生产模式编写的。
在本文中,我想实现一个很棒的开发区域,您可以尽快完成您的工作,并且还可以拥有一个采用高效标准技术的生产区域。
本文中的工具和技术
- 反应
- 反应路由器4
- 反应头盔
- CSS模块
- PostCSS
- Webpack 3
- 巴别塔
- 表达
- 下午2
基本要求
实际上,您应该安装Node
,如果没有的话,我更喜欢长期支持版本(LTS),因此,请从Node Official Website下载并安装它。
怎么办?
下一步是使文件夹成为我们想要创建的应用程序。 通过折叠项目文件,您可以调节应用程序并使其可扩展。 折叠的最佳做法是如此不同,在本文中,我使用的是最好的,但是您可以使用折叠样式。
现在,我将项目文件夹命名为react-example
,然后根据您的操作系统打开命令提示符区域或终端。 我假设你打开它。 然后使用终端进入react-example
文件夹,然后输入以下命令:
npm init
这个命令和运行一起会问您一些问题。 回答他们,但不要担心。 这个问题和答案是关于制作package.json
文件的,您可以轻松地对其进行编辑。
现在在您的终端中输入以下命令:
npm install --save [email protected] [email protected]
实际上,使用以上命令,您安装了本文的两个重要dependencies
项。 在package.json
文件旁边创建一个文件夹,名称为node_modules
,这些依赖项和其他依赖项保存在该文件夹中。 现在不在乎它们。 这些数字是本文软件包的版本。 认真使用这些版本,因为它们彼此兼容,也许当您阅读本文时,可能会发布较早的版本。
现在下一步,键入以下命令:
npm install --save-dev [email protected] [email protected] [email protected]
这些是开发区域的依赖项,-- --save-dev
安装它们,但将它们与--save
依赖项分开。 为了清楚起见,您可以打开并查看package.json
文件。
现在,该安装babel
,问题是什么?
有了一个简单的解释,您将基于最先进的技术来编写非常出色的代码,但这并不意味着当您导出项目并准备进行部署时,所有浏览器都像您一样出色。 也许用户使用的是古老的浏览器,所以,您应该照顾好它们。
Babel让您像英雄一样,但是最后,当Webpack
构建您的项目时,它会导出各种浏览器都能理解的代码,无论老少皆宜,因此,现在将以下命令粘贴到您的终端中:
npm install --save-dev [email protected] [email protected] [email protected] [email protected]
这些是babel
的插件和依赖项,现在不要关注它们,但是稍后,当您需要某些东西时,肯定会找到并找到这些插件。 要使用这些插件,必须创建一个名称为.babelrc
的文件,并在其中写入以下代码:
{
"presets": [
"env",
"es2015",
"react",
"stage-0"
]
}
以上代码的所有行都有其自身的含义。 例如es2015
意味着,让我们使用ES6
语法,在构建项目后,您将仅获得ES5.1
代码。 或react
项目系统react
以了解JSX
语法,并且在构建之后,您只会看到createElement
。 我不做更多解释,您可以搜索这些预设和插件,或者在评论中询问它们。
Webpack配置
到目前为止,所有文件和配置都是基本的,我们将所有文件和配置都放置在项目文件夹的根目录下,但是现在我们将配置放置在某些文件夹中以管理项目。 这种折叠导致项目将定期且灵活地进行更改并具有可伸缩性。
就像我说的,我们有两种环境,即Development和Production,为这个目标做一个文件夹并命名为webpack
。 在其中创建文件并将其命名为webpack.development.config.js
并按如下所示编写其内容:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const distDir = path.join(__dirname, '../dist');
const srcDir = path.join(__dirname, '../src');
module.exports = [
{
name: 'client',
target: 'web',
entry: `${srcDir}/client.jsx`,
output: {
path: path.join(__dirname, 'dist'),
filename: 'client.js',
publicPath: '/dist/',
},
resolve: {
extensions: ['.js', '.jsx']
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules[\\\/])/,
use: [
{
loader: 'babel-loader',
}
]
},
{
test: /\.pcss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[local]',
sourceMap: true,
}
},
{
loader: 'postcss-loader',
options: {
config: {
path: `${__dirname}/../postcss/postcss.config.js`,
}
}
}
]
})
},
],
},
plugins: [
new ExtractTextPlugin({
filename: 'styles.css',
allChunks: true
})
]
},
{
name: 'server',
target: 'node',
entry: `${srcDir}/server.jsx`,
output: {
path: path.join(__dirname, 'dist'),
filename: 'server.js',
libraryTarget: 'commonjs2',
publicPath: '/dist/',
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules[\\\/])/,
use: [
{
loader: 'babel-loader',
}
]
},
{
test: /\.pcss$/,
use: [
{
loader: 'isomorphic-style-loader',
},
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[local]',
sourceMap: false
}
},
{
loader: 'postcss-loader',
options: {
config: {
path: `${__dirname}/../postcss/postcss.config.js`,
}
}
}
]
}
],
},
}
];
如您所见,在此文件中,我们区分了客户端和服务器,并为每个方面设置了一些配置。 有些东西相似,有些东西不相似。 本文太长了,要获得更多解释,请在注释中询问您有关配置的问题。
PostCSS配置
很好,必须说这个Preprocessor
与其朋友(例如SCSS
是如此不同。 除安装外,还必须安装其插件。 有了插件,它将变得更强大。 而且,如果感觉到或发现存在PostCSS
无法利用它的工作,则可以搜索并找到一个插件来解决问题。 我选择了一些足够的插件,因此让我们安装它们:
npm install --save-dev [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]
在项目文件夹的根,使另一个文件夹并将其命名为postcss
,使里面一个文件作为postcss.config.js
,并把下面的配置它。 也许您会问为什么不使用SCSS或更少。 如果您稍微了解一下它的好处,那么您无疑会相信这个很棒的预处理器。 它的好处之一是自动autoprefixer
消除了您对mixins
。 只需进行一些配置,就可以更改浏览器的支持级别:
module.exports = {
ident: 'postcss',
syntax: 'postcss-scss',
map: {
'inline': true,
},
plugins: {
'postcss-partial-import': {
'prefix': '_',
'extension': '.pcss',
'glob': false,
'path': ['./../src/styles']
},
'postcss-nested-ancestors': {},
'postcss-apply': {},
'postcss-custom-properties': {},
'postcss-nested': {},
'postcss-cssnext': {
'features': {
'nesting': false
},
'warnForDuplicates': false
},
'postcss-extend': {},
'css-mqpacker': {
'sort': true
},
'autoprefixer': {
'browsers': ['last 15 versions']
},
}
};
现在的应用程序
显而易见,我们的应用程序有多个页面,并且应该存在一些管理这些页面路由的内容,在这里,我介绍我的亲爱的朋友React Router
,请注意,我想使用第4版,因此请特别安装下面的命令:
npm install --save [email protected]
然后在项目文件夹的根目录中创建一个文件夹,并将其命名为src
然后创建一个文件并将其命名为client.jsx
并在其中添加以下命令:
import React from 'react';
import {hydrate} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import App from './app/App';
hydrate((
<BrowserRouter>
<App/>
</BrowserRouter>
), document.getElementById('root'));
然后在文件上方创建另一个文件,并将其命名为server.jsx
并在其中粘贴以下代码:
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import {StaticRouter} from 'react-router-dom';
import {Helmet} from "react-helmet";
import Template from './app/template';
import App from './app/App';
export default function serverRenderer({clientStats, serverStats}) {
return (req, res, next) => {
const context = {};
const markup = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
<App/>
</StaticRouter>
);
const helmet = Helmet.renderStatic();
res.status(200).send(Template({
markup: markup,
helmet: helmet,
}));
};
};
现在,在client.jsx
和server.jsx
文件旁边创建两个文件夹,并将它们分别命名为app
和styles
。 首先是我们的主要应用程序文件,其次是CSS
样式文件。
申请文件
在app
文件夹内创建一个文件,并将其命名为template.jsx
,并将其中的主要HTML
模板放入其中, React
想要注入其标记。
export default ({ markup, helmet }) => {
return `<!DOCTYPE html>
<html ${helmet.htmlAttributes.toString()}>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id="root">${markup}</div>
<script src="/dist/client.js" async></script>
</body>
</html>`;
};
然后创建一个App.jsx
文件,它是该项目的主文件,该主文件包含我们的React Application的每个部分。 我在其中放入了一些简单的代码,但是您可以折叠并制作更大的应用程序:
import React, {Component} from 'react';
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Hello World!</h1>
</div>
);
}
}
稍微向前看,我将返回并添加更多代码,但是在看完我们简单的Hello World!
之后,它已经足够理解代码了Hello World!
浏览器中的内容又回来了,使它更加完整。
只需添加一个简单的东西, React-Helmet
,它就很棒,并且具有很好的SEO
效率。 实际上,这个很棒的组件可以动态填充head
标签,并为管理页面提供了许多选项。 要安装React-Helmet
请在终端内使用以下命令:
npm install --save [email protected]
立即安装。 当我们回到App.jsx
完成时,您会意识到它的好处。
样式文件
内部styles
文件夹中创建一个文件并将其命名为styles.pcss
。 并在其旁边创建一个文件夹并将其命名为partials
,然后在partials文件夹内部创建一个partial文件并将其命名为_partial.pcss
并在每个文件内放置以下代码:
// styles.pcss
@import "partials/partial";
.component {
@extend %box;
color: #2f95ff;
}
.text {
display: flex;
@extend %box;
}
.test {
display: flex;
}
.active {
color: red;
}
和
// partials/_partial.pcss
%box {
box-shadow: 0 0 10px 1px #ff6fc3;
}
开发服务器配置
我们开发区的服务器是express.js
。 实际上,我们使用了一些middleware
,当我们在项目的每个文件(例如PostCSS
文件)中更改某些内容时,构建系统将意识到并自动一次又一次地重建所有文件。
首先应该安装依赖包:
npm i --save-dev [email protected] [email protected] [email protected] [email protected]
然后在root内部创建一个文件夹,并将其命名为express
,并在其中放入Development配置,为此,请创建一个文件并将其命名为development.js
并按如下所示编写该文件:
const express = require('express');
const app = express();
const webpack = require('webpack');
const config = require('./../webpack/webpack.development.config.js');
const compiler = webpack(config);
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackHotServerMiddleware = require('webpack-hot-server-middleware');
app.use(webpackDevMiddleware(compiler, {
serverSideRender: true,
publicPath: "/dist/",
}));
app.use(webpackHotMiddleware(compiler.compilers.find(compiler => compiler.name === 'client')));
app.use(webpackHotServerMiddleware(compiler));
const PORT = process.env.PORT || 3000;
app.listen(PORT, error => {
if (error) {
return console.error(error);
} else {
console.log(`Development Express server running at http://localhost:${PORT}` );
}
});
让我们看看我们建造了什么
现在是时候测试并查看我们的构建了,对于此操作,需要在终端中的以下命令下运行:
node ./express/development.js
如果您使用的是Windows操作系统,则可能会看到NODE_ENV
错误,请不要担心,请访问此地址 。 我写了您应该在那里做的事情,如果没有错误,您应该在终端区域内看到以下日志 :
Development Express server running at http://localhost:3000
如果您在终端机内看到上面的日志 ,则说明您已到达我们道路的一半。 现在,在浏览器中打开http:// localhost:3000 ,并欣赏您的杰作。
反应路由器配置
到现在为止,一切都令人赞叹,我们将许多物件真正地并在一起,它们都像瑞士表一样工作。 我坚持认为,如果您有任何问题,请发表评论,我一定会回答。
如果您还记得的话,我们只是安装了React Router
并没有将其导入到应用程序中,现在是时候返回App.jsx
,现在请使用以下代码编辑该文件:
import React, {Component} from 'react';
import Helmet from "react-helmet";
import {Switch, Route} from 'react-router-dom';
import {Link, NavLink} from 'react-router-dom';
import styles from '../styles/styles.pcss';
class Menu extends Component {
render() {
return (
<div>
<ul>
<li>
<NavLink exact to={'/'} activeClassName={styles.active}>Homepage</NavLink>
</li>
<li>
<NavLink activeClassName={styles.active} to={'/about'}>About</NavLink>
</li>
<li>
<NavLink activeClassName={styles.active} to={'/contact'}>Contact</NavLink>
</li>
</ul>
</div>
);
}
}
class Homepage extends Component {
render() {
return (
<div className={styles.component}>
<Helmet title="Welcome to our Homepage"/>
<Menu/>
<h1>Homepage</h1>
</div>
);
}
}
class About extends Component {
render() {
return (
<div>
<Helmet title="About us"/>
<Menu/>
<h1>About</h1>
</div>
);
}
}
class Contact extends Component {
render() {
return (
<div>
<Helmet title="Contact us"/>
<Menu/>
<h1>Contact</h1>
</div>
);
}
}
export default class App extends Component {
render() {
return (
<div>
<Helmet
htmlAttributes={{lang: "en", amp: undefined}} // amp takes no value
titleTemplate="%s | React App"
titleAttributes={{itemprop: "name", lang: "en"}}
meta={[
{name: "description", content: "Server side rendering example"},
{name: "viewport", content: "width=device-width, initial-scale=1"},
]}
link={[{rel: "stylesheet", href: "/dist/styles.css"}]}
/>
<Switch>
<Route exact path='/' component={Homepage}/>
<Route path='/about' component={About}/>
<Route path='/contact' component={Contact}/>
</Switch>
</div>
);
}
}
听起来不错,让我解释一下这个新内容,我们有三个组件, Homepage
, About
和Contact
,它们扮演着我们三个页面的角色。
在这三个页面之后,我们制作了一个Menu
组件,用于在每个组件或实际上每个页面中显示菜单。
这里有一点 ,我们不应该像过去那样使用类名。 我们将使用CSS-Modules
,因此,我们应该像JavaScript
对象一样添加样式根文件:
import styles from '../styles/styles.pcss';
并在类名称(如JavaScript
对象)中使用它:
<div className={styles.container}>
<div className={styles['container-top']}
example
</div>
</div>
我认为这很明显,在PostCSS
文件中保留了任何类名或名称间隔方法,例如BEM
,而在JSX
内部,该名称像styles
对象的子代一样使用,而不是直接名称。
当您准备部署版本(即生产版本)时,这种使用原因是,类名称将转换为哈希名称,并且可以将它们缩小到5
字符。 当然,您构建的CSS
文件将非常小巧。
我喜欢的另一件事是React Helmet
,您可以在上面的代码中清楚地看到。 在每个组件中, React Helmet
存在;在根组件中,对于常规头部设置也存在。 这些设置非常简单,因此我拒绝在这里解释。
现在,让我们看看我们再次构建了什么,因此再次运行Development命令:
node ./express/development.js
是的,这就像一个奇迹,是基于React.js
具有许多React.js
技术的Web应用程序的框架。 当您单击每个菜单项时,相关页面将从服务器呈现,并且在浏览器中是动态的,但是完成了吗?
既肯定又否定, YES
:因为我们建立了一个真棒发展区域, NO
:因为我们不知道如何为生产区,部署做准备。
准备在生产环境上进行部署
到目前为止,我们已经为开发完成了所有工作,但是现在我们为生产确定了不同的配置,因此我们应该考虑以下三个目标:
- 捆绑和压缩应用程序中的所有文件,并销毁
debugger
和console.log
并将其移植到ES5.1
- 在一个单独的文件中提取
styles.css
并压缩和忽略所有注释 - 实际上,构建
stat.json
文件是服务器操作中的webpack
片段,express
需要它们。
使用以下命令进入生产配置:
npm install --save-dev [email protected] [email protected] [email protected]
现在在webpack
文件夹中创建另一个文件,其名称为webpack.production.config.js
。 其内容是:
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const distDir = path.join(__dirname, '../dist');
const srcDir = path.join(__dirname, '../src');
module.exports = [
{
name: 'client',
target: 'web',
entry: `${srcDir}/client.jsx`,
output: {
path: distDir,
filename: 'client.js',
publicPath: distDir,
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules[\\\/])/,
use: [
{
loader: 'babel-loader',
}
]
},
{
test: /\.pcss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[hash:base64:10]',
sourceMap: false,
}
},
{
loader: 'postcss-loader',
options: {
config: {
path: `${__dirname}/../postcss/postcss.config.js`,
}
}
}
]
})
}
],
},
plugins: [
new ExtractTextPlugin({
filename: 'styles.css',
allChunks: true
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new CleanWebpackPlugin(distDir),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
screw_ie8: true,
drop_console: true,
drop_debugger: true
}
}),
new webpack.optimize.OccurrenceOrderPlugin(),
]
},
{
name: 'server',
target: 'node',
entry: `${srcDir}/server.jsx`,
output: {
path: distDir,
filename: 'server.js',
libraryTarget: 'commonjs2',
publicPath: distDir,
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules[\\\/])/,
use: [
{
loader: 'babel-loader',
}
]
},
{
test: /\.pcss$/,
use: [
{
loader: 'isomorphic-style-loader',
},
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[hash:base64:10]',
sourceMap: false
}
},
{
loader: 'postcss-loader',
options: {
config: {
path: `${__dirname}/../postcss/postcss.config.js`,
}
}
}
]
}
],
},
plugins: [
new OptimizeCssAssetsPlugin({
cssProcessorOptions: {discardComments: {removeAll: true}}
}),
new StatsPlugin('stats.json', {
chunkModules: true,
modules: true,
chunks: true,
exclude: [/node_modules[\\\/]react/],
}),
]
}
];
然后在express
文件夹中创建另一个文件,并将其命名为production.js
并使用以下代码填充它:
const express = require('express');
const path = require('path');
const app = express();
const ClientStatsPath = path.join(__dirname, './../dist/stats.json');
const ServerRendererPath = path.join(__dirname, './../dist/server.js');
const ServerRenderer = require(ServerRendererPath).default;
const Stats = require(ClientStatsPath);
app.use('/dist', express.static(path.join(__dirname, '../dist')));
app.use(ServerRenderer(Stats));
const PORT = process.env.PORT || 3000;
app.listen(PORT, error => {
if (error) {
return console.error(error);
} else {
console.log(`Production Express server running at http://localhost:${PORT}` );
}
});
因此,现在,使用以下命令,我们可以构建生产环境所需的一些文件:
NODE_ENV=production webpack -p --config ./webpack/webpack.production.config.js --progress --profile --colors
您会看到在名为dist
的新文件夹中生成了一些文件。 您可以使用以下命令查看所有访客都会看到的半生产环境:
NODE_ENV=production node ./express/production.js
如果您在Google Chrome
浏览器上安装了React Developer Tools
和Wappalyzer
扩展,则可以看到React
徽标,尤其是可以看到React Developer Tools
变成蓝色 而且还不是红色。
蓝色表示正在生产中, 红色表示正在开发中。
恭喜你! 这篇文章很简单,但有点复杂,当您在这里时,意味着您很完美。 找到所有内容需要一些时间。 但是最后,您将使用许多其他东西(如eslint
, Redux
, Redux-Saga
或诸如Jest
测试之类的东西来创建自定义项目。
在真实服务器上运行
准备好上述所有内容后,您应该告诉DevOps
专家在生产服务器上安装pm2
,因为带有大量访问者的大型React
应用程序永远不会使用上述命令运行。 pm2
应该使用以下命令进行安装:
npm install pm2 -g
最后运行时应使用以下命令:
NODE_ENV=production pm2 start ./express/production.js
您可以在自己的PC上使用pm2
进行测试, pm2
和浏览器中的node
命令没有什么区别,但是DevOps
专家知道它们的不同,例如负载平衡,管理缓存等。如果您想了解更多关于pm2
,可以阅读其文档 。
结论
为了便于开发,我将一些命令放在package.json
文件的scripts
部分中。 您可以在GitHub存储库中访问它们或查看以下代码:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "NODE_ENV=development node ./express/development.js",
"build": "NODE_ENV=production webpack -p --config ./webpack/webpack.production.config.js --progress --profile --colors",
"prod": "NODE_ENV=production webpack -p --config ./webpack/webpack.production.config.js --progress --profile --colors && node ./express/production.js",
"pm2": "NODE_ENV=production pm2 start ./express/production.js"
}
最后一点,我在本文的所有文章中都使用了npm
是正确的,例如,您可以使用npm run dev
来运行开发,但是npm
太慢了,当文件中发生每次更改时,系统都会构建新的开发文件大约需要27
到30
秒! 后来,这很烦人。 然后,您可以使用浏览器Hard Reload
查看更改。 太无聊了。
对于这个问题,我建议您使用yarn
。 您可以使用yarn dev
命令开始进行开发,这令人难以置信,构建新的开发文件大约需要500
毫秒。
希望本文能帮助您构建 React.js
应用程序
From: https://hackernoon.com/web-application-implementation-based-on-react-js-complete-example-ae77a23b4270
以上是 基于React.js的Web应用程序实现,完整示例 的全部内容, 来源链接: utcz.com/z/383550.html