从零到一部署网站(二)

前言

上一篇文章中,我们探讨了如何利用 Nginx 和 Node.js 两种不同的方法,将静态网站部署到云服务器上。然而,一个完整的网站通常需要与后端服务进行交互,并且需要绑定域名以便用户访问。在本文中,我们将深入讲解如何在云服务器上配置网站,以便它能够与后端服务顺畅交互,并完成域名绑定的过程。

源码地址:https://github.com/baburwang/web-deploy-demo-2.git

项目准备

为了进行本次演示,我们需要准备两个项目:一个代表后端服务,另一个代表前端应用。,具体如下:

后端项目

首先,我们需要创建一个后端项目,这个项目将作为网站的后端服务,负责接收并处理来自前端的请求。

bash 复制代码
mkdir server
cd server

npm init -y
npm install express

vim index.js

index.js 文件的内容相对直接,它使用 Express 框架来启动一个 Web 服务,并能够处理以 /login 开头的 HTTP 请求。

javascript 复制代码
const express = require('express');

const app = express();

app.use(express.json());

app.post('/login', (req, res) => {
  const { username = '', password = '' } = req.body;
  if (username !== 'admin') {
    res.send({
      status: {
        code: 1,
        message: `${username} 用户不存在`
      }
    });
    return;
  }

  if (password !== '123456') {
    res.send({
      status: {
        code: 2,
        message: `密码错误`
      }
    });
    return;
  }

  res.send({
    status: {
      code: 0,
      message: '',
    },
    result: {
      id: 1,
      username: 'admin',
    }
  });
});

app.listen(8080, () => {
  console.log('Server running at http://localhost:8080')
});
bash 复制代码
nohup node index.js &

我们将后端项目文件上传到云服务器,并启动服务。随后,我们可以使用 Postman 这类工具来测试接口,确保其正常工作。

前端项目

接下来,我们需要准备一个前端项目。在初始化前端项目之后,我们将编写一个用户登录的组件,代码如下:

bash 复制代码
npm create vue@latest 
vue 复制代码
<template>
  <div class="login-form-wrapper">
    <div class="login-form-header">
      <h1>密码登录</h1>
    </div>
    <div class="login-form-body">
      <form action="/login" method="post">
        <div class="form-item">
          <input v-model="username" type="text" placeholder="输入账号" name="username" required>
        </div>
        <div class="form-item">
          <input v-model="password" type="password" placeholder="输入登录密码" name="password" required>
        </div>
      </form>
    </div>
    <div class="login-form-footer">
      <button @click="loginHandler">登录</button>
    </div>
  </div>
</template>

<script lang="ts" setup>

import { ref } from 'vue';

const username = ref<String>('');
const password = ref<String>('');

const loginHandler = () => {
  console.log('username', username);
  console.log('password', password);

  var xhr = new XMLHttpRequest();
  xhr.open('POST', 'http://43.139.228.197:8080/login', true);
  xhr.responseType = 'json';

  xhr.setRequestHeader('Content-Type', 'application/json');

  xhr.onload = function () {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log('Response:', xhr.response);
    } else {
      console.error('Request failed with status:', xhr.status);
    }
  };

  xhr.onerror = function () {
    console.error('Network error');
  };

  xhr.send(JSON.stringify({
    username: username.value,
    password: password.value,
  }));
};

</script>

<style lang="less" scoped>
  .login-form-wrapper {
    text-align: center;
    width: 300px;
    height: 300px;
    border-radius: 10px;
    box-shadow: 0 .46875vw 1.458333vw .416667vw rgba(0,0,0,.05),0 .3125vw .833333vw rgba(0,0,0,.08),0 .15625vw .3125vw -.208333vw rgba(0,0,0,.12);
    padding: 20px 0px;

    .login-form-header {
      h1 {
        color: #262626 !important;
        font-size: 1.458333vw;
        font-weight: 500;
        line-height: 1.875vw;
        text-align: center;
      }
    }

    .login-form-body {
      margin: 20px 20px;

      input {
        height: 50px;
        width: 100%;
        background-color: #eee;
        border: 1px solid transparent;
        border-radius: 10px;
        padding: 0 20px;
      }

      .form-item + .form-item {
        margin-top: 10px;
      }
    }

    .login-form-footer {
      button {
        font-size: 1.041667vw;
        font-weight: 500;
        height: 2.708333vw;
        margin: 0 auto;
        width: 14.0625vw;
        box-shadow: none;
            border-radius: 0.625vw;
            padding-left: 1.041667vw;
        padding-right: 1.041667vw;
        text-shadow: none;
        background: #262626;
        border-color: #262626;
        color: #fff;
        border: 1px solid #d9d9d9;
      }
    }
  }
</style>

开发环境

我们首先在本地开发环境中测试登录功能,当点击登录按钮时,浏览器控制台输出:Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.,这是很明显的一个跨域问题。因为我们的前端项目的地址为:http://localhost:5173,而接口请求地址为:http://43.139.228.197:8080,它们的域名和端口都不相同,因此触发了浏览器的同源策略限制。

需要明确的是,跨域是浏览器的行为,尽管服务端已经正常返回了响应结果,但浏览器出于安全考虑,阻止了网页读取这些结果。

为了在开发过程中解决接口访问的跨域问题,我们通常会利用开发服务器(devServer)的代理功能。通过配置 devServer,我们可以将前端请求代理到后端服务器,从而绕过浏览器的同源策略限制。这样,在开发阶段,我们就可以正常访问后端接口并进行调试。

1、首先,我们需要修改前端代码中的 XHR 请求地址。我们不应该直接访问后端服务器的地址 http://43.139.228.197:8080/login,而是应该将其替换为 /api/login。这样才能被devServer 所拦截。

javascript 复制代码
const loginHandler = () => {
  ...
  xhr.open('POST', '/api/login', true);
  ...
};

2、devServer 配置中代理 /api 开始的 URL 请求:

javascript 复制代码
export default defineConfig({
  plugins: [
    vue(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  server: {
    proxy: {
      '/api': {
        target: 'http://43.139.228.197:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      }
    }
  }
})

这里我们简单说下为什么使用 devServer 就不会有跨域问题?

1、首先,当点击登录时,浏览器会发送请求,此时的请求地址为 http://localhost:5173/api/login,这个请求地址中的协议、IP以及端口与网页是一致的,所以浏览器认为不存在跨域。

2、由于 http://localhost:5173 为 devServer 进程,devServer 在进程中有监听到对自己的接口请求并且是以 /api 开头的 URL,那么 devServer 则会去请求真实的后端服务然后再将响应结果返回的给前端中。

通过这样的方式,我们就解决了开发环境下的跨域请求问题。

生产环境

在生产环境中处理跨域请求需要采取与开发环境不同的策略,因为开发环境下配置的 devServer 不会在生产环境中发挥作用。

处理生产环境下的跨域请求有如下几种方式:

1、Nginx

2、Node

3、Nginx + Node

4、服务端配置 CORS(很多情况下,后端不会配置 CORS,这里略且不表)

Nginx

单纯使用 Nginx 的话,Nginx 不仅会负责托管网页,还会作为代理服务器处理接口请求。这种配置允许前端应用与后端服务之间的交互看起来像是同源的,从而绕过浏览器的跨域限制。

首先构建好前端静态资源后,并上传到云服务器下。

接着,修改 nginx 的配置文件/etc/nginx/nginx.conf,并且重启 nginx。

conf 复制代码
server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /home/lighthouse/web-depleoy-demo-2/web/dist;


        location /api/ {
            rewrite "^/api/(.*)$" /$1 break;
            proxy_pass http://43.139.228.197:8080; # 后端服务器地址和端口
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }

        access_log /var/log/nginx/access.log;
    }

1、/home/lighthouse/web-depleoy-demo-2/web/dist 项目构建后的目录。

2、location /api/ 则为代理配置,当URL以 /api/ 开始时,则会转发到 http://43.139.228.197:8080

Node

单纯使用 Node 的话,则是由 Node 托管网页,并且作为代理服务器处理接口请求。

首先,我们启动一个 web 服务进程,用来托管静态资源,同时,该 web 服务进程还可以进行接口代理。

bash 复制代码
mkdir server
cd server

npm init -y

npm install express
npm install http-proxy-middleware

vim app.js

app.js的内容如下,分别实现了网页托管以及接口代理。

javascript 复制代码
const path = require('path');
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// 静态资源托管
app.use(express.static(path.resolve(__dirname, 'dist')));

// 接口代理
app.use('/api', createProxyMiddleware({
  target: 'http://43.139.228.197:8080',
  pathRewrite: {
    '^/api': '' // 重写路径,去除 /api 前缀
  },
  changeOrigin: true
}));

app.listen(3000, () => {
  console.log(`Server is start, port is 3000`);
});

Nginx + Node

结合使用 Nginx 和 Node.js 是一种更为推荐的部署方式,Nginx 具有非常强大的处理性能,并且通过 Node 可以具有更加灵活的实现。

  • Nginx 负责接口代理
  • Node 负责静态资源托管

具体实现方案如下,用户访问的入口为 Nginx 所在的进程,Nginx 不仅会负责接口代理,还会将前端资源的访问请求代理到托管前端资源的服务下。

ini 复制代码
    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        # root         /usr/share/nginx/html;
        # root         /home/lighthouse/web-depleoy-demo-2/web/dist;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;


        location /api/ {
            rewrite "^/api/(.*)$" /$1 break;
            proxy_pass http://43.139.228.197:8080; # 后端服务器地址和端口
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        location / {
            proxy_pass http://43.139.228.197:3000; # 前端web服务器托管地址和端口
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }

        access_log /var/log/nginx/access.log;
    }

这部分演示代码已上传至 github,可 clone 下来之后直接运行。

bash 复制代码
git clone https://github.com/baburwang/web-deploy-demo-2.git

cd web-deploy-demo-2
sh deploy.sh

域名绑定

域名绑定的流程相对比较简单,我们在购买后域名后,直接在云服务器选择域名解析,可以通过域名访问了。

需要注意的是,如果没有完成域名备案,访问时可能会被拦截。

总结

通过这两篇文章,想必你一定对网站的部署有了更为深刻的认识。如果你对于以上内容以及其他前端技能存在困惑,欢迎在评论区留言或添加我的个人微信交流。

相关推荐
m0_748247551 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255022 小时前
前端常用算法集合
前端·算法
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203982 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2343 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1233 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~4 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语4 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport4 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg4 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全