如何正确将create-react-app构建的ReactJS应用部署到Azure Web App?
规范部署Create React App到Azure Web App的正确方式
我来帮你梳理下更标准的部署方案,针对Create React App这类单页应用(SPA),Azure提供了两种更合适的方式,比你目前依赖serve的方法更符合最佳实践:
方案一:Azure静态网站托管(首推)
这是最适合SPA的部署方式,因为Create React App构建后产出的是纯静态文件,完全不需要Node.js服务器支撑:
- 先在本地执行构建命令生成静态文件:
npm run build - 登录Azure门户,创建静态网站资源(属于存储账户下的功能)
- 将
build文件夹里的所有内容上传到静态网站的$web容器(可以用Azure Storage Explorer可视化操作,或者用Azure CLI命令批量上传) - 关键配置:设置路由规则,因为SPA需要把所有非静态资源的请求重定向到
index.html,否则刷新页面会404。在静态网站的设置里添加一条规则:- 源路径:
/* - 目标路径:
/index.html - 类型:
200
- 源路径:
这种方式成本更低、性能更优,完全贴合SPA的特性,是官方推荐的部署方式。
方案二:Azure Web App(适合需联动后端或Web App功能的场景)
如果因为业务需求必须用Azure Web App,也不需要依赖serve,可以利用Azure Web App自带的IIS静态文件服务和URL重写规则来实现:
第一步:添加Web.config配置文件
在你的React项目根目录创建web.config文件,添加URL重写规则,确保所有非静态文件的请求都转发到index.html:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="React Routes" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> <!-- 如果你的项目有API路由,添加这一行排除API请求,避免被重写 --> <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" /> </conditions> <action type="Rewrite" url="/index.html" /> </rule> </rules> </rewrite> <!-- 确保一些特殊后缀的文件能被正确识别 --> <staticContent> <mimeMap fileExtension=".json" mimeType="application/json" /> <mimeMap fileExtension=".svg" mimeType="image/svg+xml" /> </staticContent> </system.webServer> </configuration>
第二步:优化你的deploy.sh脚本
修改部署脚本,确保在部署过程中完成依赖安装、项目构建,然后将build文件夹的内容同步到wwwroot:
#!/bin/bash # ---------------------- # KUDU Deployment Script # Version: 1.0.17 # ---------------------- # Helpers # ------- exitWithMessageOnError () { if [ ! $? -eq 0 ]; then echo "An error has occurred during web site deployment." echo $1 exit 1 fi } # Prerequisites # ------------- # Verify node.js installed hash node 2>/dev/null exitWithMessageOnError "Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment." # Setup # ----- SCRIPT_DIR="${BASH_SOURCE[0]%\*}" SCRIPT_DIR="${SCRIPT_DIR%/*}" ARTIFACTS=$SCRIPT_DIR/../artifacts KUDU_SYNC_CMD=${KUDU_SYNC_CMD//"} if [[ ! -n "$DEPLOYMENT_SOURCE" ]]; then DEPLOYMENT_SOURCE=$SCRIPT_DIR fi if [[ ! -n "$NEXT_MANIFEST_PATH" ]]; then NEXT_MANIFEST_PATH=$ARTIFACTS/manifest if [[ ! -n "$PREVIOUS_MANIFEST_PATH" ]]; then PREVIOUS_MANIFEST_PATH=$NEXT_MANIFEST_PATH fi fi if [[ ! -n "$DEPLOYMENT_TARGET" ]]; then DEPLOYMENT_TARGET=$ARTIFACTS/wwwroot else KUDU_SERVICE=true fi if [[ ! -n "$KUDU_SYNC_CMD" ]]; then # Install kudu sync echo Installing Kudu Sync npm install kudusync -g --silent exitWithMessageOnError "npm failed" if [[ ! -n "$KUDU_SERVICE" ]]; then # In case we are running locally this is the correct location of kuduSync KUDU_SYNC_CMD=kuduSync else # In case we are running on kudu service this is the correct location of kuduSync KUDU_SYNC_CMD=$APPDATA/npm/node_modules/kuduSync/bin/kuduSync fi fi # Node Helpers # ------------ selectNodeVersion () { if [[ -n "$KUDU_SELECT_NODE_VERSION_CMD" ]]; then SELECT_NODE_VERSION="$KUDU_SELECT_NODE_VERSION_CMD \"$DEPLOYMENT_SOURCE\" \"$DEPLOYMENT_TARGET\" \"$DEPLOYMENT_TEMP\"" eval $SELECT_NODE_VERSION exitWithMessageOnError "select node version failed" if [[ -e "$DEPLOYMENT_TEMP/__nodeVersion.tmp" ]]; then NODE_EXE=`cat "$DEPLOYMENT_TEMP/__nodeVersion.tmp"` exitWithMessageOnError "getting node version failed" fi if [[ -e "$DEPLOYMENT_TEMP/__npmVersion.tmp" ]]; then NPM_JS_PATH=`cat "$DEPLOYMENT_TEMP/__npmVersion.tmp"` exitWithMessageOnError "getting npm version failed" fi if [[ ! -n "$NODE_EXE" ]]; then NODE_EXE=node fi NPM_CMD=\"$NODE_EXE\" \"$NPM_JS_PATH\" else NPM_CMD=npm NODE_EXE=node fi } ################################################################################################################################## # Deployment # ---------- echo Handling node.js deployment. # 2. Select node version (保留自动选择版本的逻辑,避免本地与Azure版本不一致) selectNodeVersion # 3. Install npm packages (需要安装所有依赖,包括dev依赖,因为要执行build) if [ -e "$DEPLOYMENT_SOURCE/package.json" ]; then cd "$DEPLOYMENT_SOURCE" echo "Running $NPM_CMD install" eval $NPM_CMD install exitWithMessageOnError "npm install failed" # 执行build命令生成静态文件 echo "Running $NPM_CMD run build" eval $NPM_CMD run build exitWithMessageOnError "build failed" cd - > /dev/null fi # 1. KuduSync: 将build文件夹内容同步到wwwroot if [[ "$IN_PLACE_DEPLOYMENT" -ne "1" ]]; then "$KUDU_SYNC_CMD" -v 50 -f "$DEPLOYMENT_SOURCE/build" -t "$DEPLOYMENT_TARGET" -n "$NEXT_MANIFEST_PATH" -p "$PREVIOUS_MANIFEST_PATH" -i ".git;.hg;.deployment;deploy.sh" exitWithMessageOnError "Kudu Sync failed" fi ################################################################################################################################## echo "Finished successfully."
第三步:配置Azure Web App
- 不需要设置启动命令,Azure Web App会自动通过IIS托管
wwwroot里的静态文件,web.config的重写规则会处理SPA的路由问题 - 在Azure门户的Web App配置>常规设置里,设置Node.js版本和本地项目使用的版本一致,避免构建时出现兼容性问题
为什么你的原有方法不够规范?
- 额外引入
serve依赖,增加了部署复杂度和不必要的服务器资源消耗 - 用Node.js服务器托管静态文件,不如Azure Web App自带的IIS静态文件服务高效
- 不符合SPA的部署最佳实践:纯静态文件应该直接由专门的静态文件服务托管,不需要额外的Node.js中间层
内容的提问来源于stack exchange,提问作者AQuirky
相关产品推荐
相关产品推荐

