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