搭建
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