携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
搭建
react开发环境为全新环境,这里只讲搭建react环境,不再包含less、css等,只要不使用这些东西一样可以构建出来,因为这些之前都有讲过,再写就有点凑字数和废话,好学的可以看历史章节。
- 历史章节
搭建react环境
这次搭建为从零开始,不包含开发环境,而且直接跳过
.jsx,直接使用.tsx。
- 新建一个项目(就是目录),随便取个名字。
npm init -y生成一个package.json。- 初始化
webpack,命令行npm i -D webpack webpack-cli - 根目录下创建
webpack.config.js,内容如下
const path = require("path");
module.exports = {
mode: "production",
entry: './src/index.tsx',
output: {
filename: "[name].js",
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /.tsx$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
[
'@babel/preset-react',
{ pragma: 'createElement' },
],
],
},
},
{ loader: 'ts-loader' },
],
},
]
},
resolve: {
extensions: ['.tsx'], // 这个可以让webpack默认入口的文件类型(.js/.json/.wasm)新增额外类型
}
}
从上面的配置可以看到我们要安装解析
.tsx的配置,需要安装很多依赖,babel-loader、@babel/preset-env、@babel/preset-react、ts-loader等。
- 安装
babel:npm i -D babel-loader @babel/core @babel/preset-env - 安装
ts:npm i -D typescript ts-loader - 安装
babel对react的支持:npm i -D @babel/preset-react @babel/plugin-transform-react-jsx - 安装
react:npm i react(这里没有加-D,因为react是要放在生产环境中的,但是课程中都是有-D的,而且确实加不加都能构建出来,都能运行)
上面的依赖就是目前
recat环境所需要的全部依赖了,如果你还需要使用css,实际项目中是一定需要的,就需要安装解析css的相关依赖了,还需要加上对应的配置,这个之前章节讲过了,就没有相关的代码和演示。
- 新建
tsconfig.json配置,内容如下
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"sourceMap": true,
"jsx": "react"
},
"exclude": [
"node_modules"
]
}
- 新建
src目录 src下面新建index.tsx,内容如下
import React from 'react';
const Component = () => {
return <div className="hello">hello world</div>
}
- 运行
npx webpack构建项目,可以看到会生成dist目录
可以增加一个
html-webpack-plugin生成.html文件,用来查看构建出来的内容是否正确,也可以增加一个webpack-dev-server试试开发环境和热更新效果。
以上就完成了对react环境的搭建
react服务端渲染
将
Vue服务端渲染时已经介绍过服务端渲染的概念,所以这里略过。recat服务端渲染和Vue类似,所以目录结构都是一样的,还是还是用一个新的工程来记录。
- 创建
react-ssr目录 npm init -y生成package.json- 根目录下新建
src目录 src下面新建entry-client.tsx,内容如下
import { createRoot } from 'react-dom/client';
import App from './App';
const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App />);
src下面新建entry-server.tsx,内容如下
import React from 'react'
import App from './App'
import { renderToString } from 'react-dom/server';
export default () => {
return renderToString(<App/>);
}
src下面新建App.tsx,内容如下
import React, { useState } from 'react';
import './App.css';
const App = () => {
const [isActivity, setIsActivity] = useState(false);
const handleClick = () => {
setIsActivity(!isActivity);
};
return (
<div>
<h3 className={`main ${isActivity ? 'activate' : 'deactivate'}`}>Hello World</h3>
<button onClick={handleClick}>Toggle</button>
</div>
);
};
export default App;
src下面新建App.css,内容如下
h3 {
color: #42b983;
}
.main {
padding: 20px 12px;
transition: background 0.3s linear;
}
.activate {
background: #000;
}
.deactivate {
background: #fff;
}
上面已经完成了对
react的页面构建,这一块和Vue的服务端渲染很相似,接下来就是对webpack的配置
- 根目录下新建
webpack.base.js,内容如下
module.exports = {
mode: "production",
module: {
rules: [
{
test: /.tsx$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
[
'@babel/preset-react',
{ pragma: 'createElement' },
],
],
},
},
{ loader: 'ts-loader' },
],
},
]
},
resolve: {
extensions: ['.js', '.jsx', '.tsx'],
}
}
上面的配置就是上面讲
react环境的配置,没想到这么快就用上了,而且还是作为基础配置来使用的。
- 根目录下新建
webpack.client.js(客户端文件生成的webpack配置),内容如下
const Merge = require("webpack-merge");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const base = require("./webpack.base");
// 继承自 `webpack.base.js`
module.exports = Merge.merge(base, {
entry: {
// 入口指向 `entry-client.js` 文件
client: path.join(__dirname, "./src/entry-client.tsx"),
},
output: {
filename: 'index.js',
publicPath: "/",
},
module: {
rules: [{ test: /.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] }],
},
plugins: [
// 这里使用 webpack-manifest-plugin 记录产物分布情况
// 方面后续在 `server.js` 中使用
new WebpackManifestPlugin({ fileName: "manifest-client.json" }),
// 生成CSS文件
new MiniCssExtractPlugin({
filename: 'index.[contenthash].css'
}),
// 自动生成 HTML 文件内容
new HtmlWebpackPlugin({
templateContent: `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack App</title>
</head>
<body>
<div id="app" />
</body>
</html>
`,
}),
],
});
- 根目录下新建
webpack.server.js(服务端文件生成的webpack配置),内容如下
const Merge = require("webpack-merge");
const path = require("path");
const base = require("./webpack.base");
module.exports = Merge.merge(base, {
entry: {
server: path.join(__dirname, "./src/entry-server.tsx"),
},
target: "node",
output: {
// 打包后的结果会在 node 环境使用
// 因此此处将模块化语句转译为 commonjs 形式
libraryTarget: "commonjs2",
filename: 'server.js'
},
module: {
rules: [{
test: /.css$/,
loader: './loader/removeCssLoader'
}]
},
});
这里有一个细节,就是
.css的loader是自己的写的,因为服务端只需要生成页面,不需要管css的生成,这里会写一个空的css-loader。
- 根目录下新建目录
loader loader目录下新建文件removeCssLoader.js,内容如下
module.exports = () => {
return 'module.exports = () => null';
};
- 根目录下新建
tsconfig.json,内容如下
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"sourceMap": true,
"jsx": "react-jsx"
},
"exclude": [
"node_modules"
]
}
课程里面讲的
js版本的,我这里实践是ts版本的,所以会多一点东西。
接下来就是构建项目了,可以使用npx webpack --config webpack.client.js和npx webpack --config webpack.server.js分别构建出客户端资源和服务端资源,我这里还是将这两个命令放到了package.json文件中,如下
"scripts": {
"build:client": "npx webpack --config ./webpack.client.js",
"build:server": "npx webpack --config ./webpack.server.js",
},
接下来就可以使用npm run build:client和npm run build:server来构建客户端和服务端资源了。
搭建node服务验证效果
这里还是和
vue服务端渲染一样,使用express来作为服务端。
- 在根目录下新建
server.js,内容如下
const express = require('express');
// 通过 manifest 文件,找到正确的产物路径
const clientManifest = require("./dist/manifest-client.json");
const server = express();
server.get("/", (req, res) => {
const html = require('./dist/server.js').default;
const clientCss = clientManifest["client.css"];
const clientBundle = clientManifest["client.js"];
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>React SSR Example</title>
<link rel="stylesheet" href="${clientCss}"></link>
</head>
<body>
<!-- 注入组件运行结果 -->
<div id="app">${html()}</div>
<!-- 注入客户端代码产物路径 -->
<!-- 实现 Hydrate 效果 -->
<script src="${clientBundle}"></script>
</body>
</html>
`);
});
server.use(express.static("./dist"));
server.listen(3000, () => {
console.log("服务启动成功:http://localhost:3000");
});
接下来就可以运行node server.js来启动服务啦,但是为了方便我这里将所有的命令一站式封装到package.json中了,这里还是因为依赖太多,所以服务端渲染这一块没有讲依赖安装的环节,这里还是直接上package.json
{
"name": "react-ssr",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build:client": "npx webpack --config ./webpack.client.js",
"build:server": "npx webpack --config ./webpack.server.js",
"server": "node ./dist/server.js",
"run": "npm run build:client && npm run build:server && node server.js",
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.18.10",
"@babel/plugin-transform-react-jsx": "^7.18.10",
"@babel/preset-env": "^7.18.10",
"@babel/preset-react": "^7.18.6",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"express": "^4.18.1",
"html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "^2.6.1",
"ts-loader": "^9.3.1",
"typescript": "^4.7.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-manifest-plugin": "^5.0.0",
"webpack-merge": "^5.8.0"
}
}
现在可以使用npm run run来一站式构建客户端、服务端资源和启动服务啦,瑞思拜。
总结
没什么好总结的,今天不上课程里面的总结了,Vue的服务端渲染是直接对着课程来的,很顺利。
react的服务端渲染对着课程来报错了,不是因为我使用ts版的问题,是构建出来的资源无法运行,后来将架构改成和Vue版一致的就好了。
使用ts版的也是个人的拓展练习,准备webpack学完就深入ts了。
课程中建议这些东西不要自己折腾,可以直接使用比较成熟的Create React App,当然使用归使用,练习和学习还是自己来,这样就可以知其然且知其所以然。
示例代码:react-ssr