一键搭建Web前后端开发服务

搭建一个网站开发框架并不简单,至少要满足以下几个要点:

  • 前端:
    • 要有Webpack编译服务,支持ES6语法,能npm install,能import
    • 开发模式自动更新,改一下JS代码页面能马上自动刷新,有热更新HMR
    • 能够顺利访问后端API服务获取数据,方便调试接口
  • 后端:
    • 静态文件服务npm run build编译好的页面要能正常访问,往往要配置Nginx、Apache
    • 动态开发路由服务npm run dev的时候也能访问实时更新
    • 自带接口API路由服务,让前端能够调试API,获取数据

对于新手来说,安装node、npm和golang并不困难,但配置webpack.config.json一大堆就很头疼,然后还要配置Nginx静态文件服务和接口代理、开发代理,然后再配置Golang开发环境,搞不好几个小时就没了,可能没熬到最后成功,大部分就放弃了...

如果你是在MacOS苹果系统下想快速搭建一个网站开发框架,可以复制下面代码,保存为a.sh然后终端执行sh a.sh,输入任意文件夹名比如myweb,十几秒,满足以上要点的完整前后端框架就运行起来了,你可以立即编写前端web/src/index.js代码,页面自动刷新,也可以直接编写main.go撰写自己的api接口,然后go run main.go1秒重启API服务。

运行效果

推荐直接在VSCode中运行sh命令,默认会弹出终端窗口启动页面开发测试服务,运行在8080端口。你可以随时关掉它,进入web文件夹,npm run dev重新启动。

Golang服务器默认运行在8081端口,会自动代理静态文件到dev服务器,如果需要代理到build好的静态服务器,请自行修改代码http.HandleFunc("/api", APIHandler)变为http.HandleFunc("/api", StaticFileHandler),重启即可,要做为正式服务器使用,你需要修改端口8081变为80。

不需要Nginx或者其他,这个Golang程序自带网站静态文件路由功能,无需任何设置。这样做的另一个好处就是文件服务和API接口都由Golang运行在同一端口,可以避免跨域不同源问题。

确认node、npm、go安装完成且命令可用,然后复制下面代码,保存为a.sh放到任意目录,sh a.sh运行。

#!/bin/bash
# 需要修改为可执行文件 chmod +x CreateGoWebProject.sh
# 然后 sh CreateGoWebProject.sh

# Part1:创建golang项目

# 切换到国内镜像
go env -w GO111MODULE=on
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct

# 提示用户输入项目名称
read -p "请输入项目名称: " project_name

# 检查用户是否输入了项目名称
if [ -z "$project_name" ]; then
  echo "项目名称不能为空。"
  exit 1
fi

# 创建项目目录并进入
mkdir "$project_name" && cd "$project_name"

# 初始化项目
go mod init "$project_name"

# 创建main.go文件
echo 'package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/http/httputil"
    "net/url"
    "os"
    "path/filepath"
)

func main() {
    // 创建路由器
    http.HandleFunc("/api", APIHandler)
    http.HandleFunc("/", ProxyHandler)

    // 启动服务器
    fmt.Println("Server is running at http://localhost:8081")
    if err := http.ListenAndServe(":8081", nil); err != nil {
        fmt.Printf("Error starting server: %v", err)
    }
}

// APIHandler 处理 /api 请求
func APIHandler(w http.ResponseWriter, r *http.Request) {
    jsonData, _ := json.Marshal(map[string]string{
        "message": "API response",
        "status":  "200",
    })
    w.Header().Set("Content-Type", "application/json")
    w.Write(jsonData)
}

// ProxyHandler 处理所有其他请求并代理到 localhost:8080
func ProxyHandler(w http.ResponseWriter, r *http.Request) {
    target := "http://localhost:8080"
    targetURL, _ := url.Parse(target)
    proxy := httputil.NewSingleHostReverseProxy(targetURL)
    proxy.ServeHTTP(w, r)
}

// StaticFileHandler 处理所有其他请求并提供静态文件服务,正式服务器使用
func StaticFileHandler(w http.ResponseWriter, r *http.Request) {
    staticDir := "./web/dist"
    filePath := filepath.Join(staticDir, r.URL.Path)
    // 检查文件是否存在
    if _, err := os.Stat(filePath); os.IsNotExist(err) {
        http.NotFound(w, r)
        return
    }
    // 提供静态文件服务
    http.ServeFile(w, r, filePath)
}'> main.go

# Part2:创建ES6-Web项目

# 创建项目目录并进入
mkdir "web" && cd "web"

# 初始化 npm 项目
npm init -y

# 安装开发所需的依赖
npm install --save-dev @babel/core @babel/cli @babel/preset-env webpack webpack-cli webpack-dev-server babel-loader html-webpack-plugin

# 创建 Babel 配置文件
echo '{
  "presets": ["@babel/preset-env"]
}' > .babelrc

# 创建 Webpack 配置文件
echo 'const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
  devServer: {
    static: {
      directory: path.join(__dirname, "dist"),
    },
    compress: true,
    port: 8080,
    open: false, // 自动打开浏览器
    client: {
      logging: "error", // 禁用客户端日志
    },
    devMiddleware: {
      stats: "errors-only", // 只输出错误信息
    },
  },
  mode: "development",
};' > webpack.config.js

# 创建项目结构
mkdir src
echo 'if (!window.isInitialized) {
    window.isInitialized = true
    const greet = async () => {
        const hellostr = `Hello ES6!`
        const root = document.getElementById(`root`)
        root.innerHTML = `<h1>${hellostr}</h1><p>${new Date()}</p>`
        console.log(hellostr)

        fetch(`/api`)
            .then(resp => resp.json())
            .then((resp) => {
                console.log(`/api:`, resp)
            })
    }
    greet()
}' > src/index.js

echo '<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ES6 Project</title>
</head>

<body>
    <div id="root"></div>
    <script src="bundle.js"></script>
</body>

</html>' > src/index.html

# 添加构建和启动脚本到 package.json
npx json -I -f package.json -e 'this.scripts={"build":"webpack","dev":"webpack serve"}'



# Part3:启动服务

# 查找并杀死占用8080端口的进程
for port in 8080 8081; do
  echo "Killing processes on port $port..."
  lsof -i tcp:$port | grep LISTEN | awk '{print $2}' | xargs kill -9
done



# 新窗口启动Web开放服务器
CUR_PATH=$(pwd)
osascript <<EOF
tell application "Terminal"
    do script "cd \"$CUR_PATH\" && npm run dev"
    activate
end tell
EOF

# 启动Golang服务器
echo "\n站点服务正在启动,请打开下面网址查看:http://localhost:8081"
cd .. && go run main.go


END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容