从零到一部署网站(二)

前言

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

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

项目准备

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

后端项目

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

mkdir server
cd server

npm init -y
npm install express

vim index.js

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

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')
});
nohup node index.js &

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

image.png

前端项目

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

npm create vue@latest 
<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>

image.png

开发环境

         我们首先在本地开发环境中测试登录功能,当点击登录按钮时,浏览器控制台输出: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,它们的域名和端口都不相同,因此触发了浏览器的同源策略限制。

image.png

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

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

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

const loginHandler = () => {
  ...
  xhr.open('POST', '/api/login', true);
  ...
};

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

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

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

image.png

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

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

image.png

Node

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

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

mkdir server
cd server

npm init -y

npm install express
npm install http-proxy-middleware

vim app.js

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

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`);
});

image.png

Nginx + Node

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

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

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

    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 下来之后直接运行。

git clone https://github.com/baburwang/web-deploy-demo-2.git

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

image.png

域名绑定

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

image.png

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

image.png

总结

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

IMG_80A884E81764-1.jpeg

  • 46
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值