Javascript网络编程学习报告

Vue组件化响应式编程介绍

        Vue.js是一款流行的JavaScript框架,它支持组件化和响应式编程。组件化是一种将用户界面划分为独立可复用的模块的开发方式,而响应式编程则允许数据的变化自动更新视图,提高开发效率。Vue.js巧妙地结合了这两个概念,下面将介绍Vue组件化和响应式编程的关键特性。

        Vue组件化使得应用程序的开发更加模块化和可维护。一个Vue应用由一个个小的组件组成,每个组件都有自己的状态和行为。这种模块化的开发方式使得团队可以更容易地分工合作,每个成员专注于开发和维护特定的组件,而不必担心整个应用的复杂性。组件可以嵌套在其他组件中,形成清晰的层次结构,使得代码更具可读性和可扩展性。

        Vue的响应式编程机制使得数据与视图之间建立了自动的关联。当数据发生变化时,与之相关的视图会自动更新,而无需手动操作DOM。这是通过Vue的数据绑定和响应式系统实现的。开发者只需要关注数据的变化,Vue会负责更新与之相关联的视图,大大简化了开发流程。这种响应式的特性使得开发者能够更专注于业务逻辑,而不必过多考虑视图的更新问题。

        在Vue中,一个组件通常由三个部分组成:模板(Template)、脚本(Script)和样式(Style)。模板定义了组件的结构,脚本包含了组件的逻辑和状态,样式则定义了组件的外观。这三者通过Vue的单文件组件(.vue文件)的形式进行组织,使得组件的代码更加清晰有序。

        在脚本部分,开发者可以使用Vue提供的数据绑定语法,将数据与视图进行关联。当数据发生变化时,相关的视图会自动更新,实现了响应式编程。开发者还可以通过计算属性(computed properties)和监听器(watchers)等特性来更灵活地控制数据和视图之间的关系。

Vue中的异步调用

        异步调用在Vue中的应用是前端开发中不可或缺的一部分,它涉及到处理异步操作,如数据请求、定时器、事件处理等。Vue结合了各种异步处理方式,包括Callback函数、Promise对象以及async/await等,以便更好地管理和组织异步代码。

        Callback函数是一种基本的异步处理方式,它在Vue中被广泛应用。通过Callback函数,可以定义一个回调函数,在异步操作完成后调用该函数。在Vue中,常见的异步操作包括数据请求,比如使用Vue的$http或者axios库来获取数据。通过Callback函数,可以在异步数据加载完成后执行相应的逻辑,确保数据准备就绪后再进行界面更新。例如:

created() {
  this.getData(this.handleData);
},

methods: {
  getData(callback) {
    // 异步请求数据
    this.$http.get('/api/data').then(response => {
      // 数据加载完成后执行回调
      callback(response.data);
    });
  },

  handleData(data) {
    // 处理数据逻辑
    this.data = data;
  }
}

        其次,Promise对象是一种更为灵活和可组合的异步处理方式,它在Vue中也得到广泛应用。Promise对象可以处理复杂的异步操作流程,使得代码更加清晰和易于维护。例如,在数据请求中使用Promise对象可以更好地处理异步链式调用:

created() {
  this.getData()
    .then(data => {
      // 处理数据逻辑
      this.data = data;
    })
    .catch(error => {
      // 处理错误逻辑
      console.error(error);
    });
},

methods: {
  getData() {
    // 返回一个Promise对象
    return new Promise((resolve, reject) => {
      this.$http.get('/api/data').then(response => {
        // 异步请求成功,调用resolve传递数据
        resolve(response.data);
      }).catch(error => {
        // 异步请求失败,调用reject传递错误信息
        reject(error);
      });
    });
  }
}

Vue中的Ajax

        在Vue.js中,通常会使用ajax(Asynchronous JavaScript and XML)来进行异步数据请求,而axios是一个强大且广泛使用的HTTP客户端库,用于发起HTTP请求。在Vue项目中,对axios进行封装是一种常见的实践,这样能够更好地组织和管理代码,提高代码的可维护性和可复用性。

        封装axios的一个主要目的是为了在应用中创建自定义的API调用函数,使其更符合项目的需求,并提供一致的接口。以下是一个Vue中axios封装的例子:

// 在src/utils/http.js中封装axios
import axios from 'axios';

// 创建axios实例
const instance = axios.create({
  baseURL: process.env.VUE_APP_API_BASE_URL, // API基础路径
  timeout: 5000, // 请求超时时间
});

// 封装GET请求
export const get = (url, params) => {
  return instance.get(url, { params });
};

// 封装POST请求
export const post = (url, data) => {
  return instance.post(url, data);
};

// 在Vue组件中使用
import { get, post } from '@/utils/http';

export default {
  created() {
    // 发起GET请求
    get('/api/data', { param1: 'value1' })
      .then(response => {
        // 处理响应数据
        console.log(response.data);
      })
      .catch(error => {
        // 处理错误
        console.error(error);
      });

    // 发起POST请求
    post('/api/postData', { key: 'value' })
      .then(response => {
        // 处理响应数据
        console.log(response.data);
      })
      .catch(error => {
        // 处理错误
        console.error(error);
      });
  },
};

专题实验:一个简单的 Vue + SpringBoot 的在线多人匿名聊天的网站

前端登录界面的搭建

登录通过昵称登录,其中的template标签代码如下:

<template>
  <div class="name">
    <el-form
      class="name-form"
      ref="nameFormRef"
      :model="nameForm"
      :rules="nameRules"
      @submit.prevent
    >
      <el-form-item>
        <h1>多人群聊系统</h1>
      </el-form-item>
      <el-form-item class="name-input" prop="nickname">
        <el-input
          class="nickname"
          v-model="nameForm.nickname"
          @keydown.enter="submitButton(nameFormRef)"
          placeholder="输入一个昵称"
        ></el-input>
        <el-button
          class="submit"
          type="primary"
          @click="submitButton(nameFormRef)"
          >进入</el-button
        >
      </el-form-item>
    </el-form>
  </div>
</template>

其中用户名从前端传递到后端,后端服务器对其进行校验,若不存在则将username加入session,否则,前端判空,提示用户,用户名重复。

前端代码如下:

// 昵称表单
const nameFormRef = ref<FormInstance>();

// 昵称表单
const nameForm = reactive({
  nickname: "",
});

// 表单校验
const nameRules = reactive<FormRules>({
  nickname: [{ required: true, message: "请输入一个昵称", trigger: "blur" }],
});

// 登录操作
const submitButton = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.validate((valid) => {
    if (valid) {
      axios
        .get("http://localhost:8888/list/" + nameForm.nickname)
        .then((resp) => {
          const data = resp.data;

          // 判断用户名是否存在
          if (!data.isExist) {
            sessionStorage.setItem("name", nameForm.nickname);
            router.push("/chat");
          } else {
            ElMessage({
              message: "该用户名已存在,请更换",
              grouping: true,
              type: "error",
            });
          }
        });
    } else {
      ElMessage({
        message: "请输入一个昵称",
        grouping: true,
        type: "error",
      });
      return false;
    }
  });
};

后端控制器代码如下:

@GetMapping("/{username}")
    public JSONObject getUsername(@PathVariable("username") String username) {
        JSONObject jsonObject = new JSONObject();
        boolean isEmpty = sessionMap.isEmpty();
        jsonObject.put("isEmpty", isEmpty);
        jsonObject.put("isExist", false);
        if (!isEmpty) {
            boolean isExist = sessionMap.containsKey(username);
            jsonObject.replace("isExist", isExist);
        }
        return jsonObject;
    }

 (后端开放8888端口供前端访问,把登录的用户存到 sessionMap 中)

通过websocket访问后端数据

        WebSocket是一种在单个持久连接上进行全双工通信的协议,它能够在客户端和服务器之间实现实时、低延迟的数据传输。在Java中,有多种方式可以实现WebSocket通信,其中比较常用的是使用Java API for WebSocket(JSR-356)和Spring框架提供的支持。        

前端需要开启websocket服务,绑定后端服务端口,设置url地址。

  // 开启 WebSocket 服务
  let socketHost = "localhost";
  let socketPort = "8888";
  let socketUrl =
    "ws://" + socketHost + ":" + socketPort + "/socket/" + nickname.value;
  socket = new WebSocket(socketUrl);

前端设置websocket的代码如下:

// 连接服务器
  socket.onopen = () => {
    console.log("已连接至服务器");
  };

  // 浏览器接收服务端发送的消息
  socket.onmessage = (msg) => {
    let data = JSON.parse(msg.data);
    if (data.userlist) {
      // 接收用户列表消息
      userList.value = data.userlist;
      userCount.value = data.userlist.length;
    } else {
      // 接收消息
      messages.value.push(data);

      // 获取节点
      let chatHistory = document.getElementsByClassName("chat-message")[0];
      if (chatHistory.scrollHeight >= chatHistory.clientHeight) {
        setTimeout(function () {
          //设置滚动条到最底部
          chatHistory.scrollTop = chatHistory.scrollHeight;
        }, 0);
      }
    }
  };
  // 关闭服务
  socket.onclose = () => {
    console.log("WebSocket 服务已关闭");
  };
  // 错误事件
  socket.onerror = () => {
    console.log("WebSocket 服务发生错误");
  };
});

服务器端有四个方法:

       @OnOpen   //当链接创建时调用的

       @OnMessage  //当接收到消息时调用的

       @OnClose   //当链接关闭时调用的

       @OnError  //当链接发生异常时调用的

其中onopen,onclose,onmessage,onerror,在服务端对应的代码如下:

@OnOpen
    public void onOpen(Session session, @PathParam("username") String username) {

        // 搜索名称是否存在
        boolean isExist = sessionMap.containsKey(username);
        if (!isExist) {
            System.out.println(username + "加入了聊天室");
            sessionMap.put(username, session);
            sendAllMessage(setUserList());
            showUserList();
        }
    }

    /**
     * WebSocket 关闭连接事件
     * 1.把登出的用户从 sessionMap 中剃除
     * 2.发送给所有人当前登录人员信息
     */
    @OnClose
    public void onClose(@PathParam("username") String username) {
        if (username != null) {
            System.out.println(username + "退出了聊天室");
            sessionMap.remove(username);
            sendAllMessage(setUserList());
            showUserList();
        }
    }

    /**
     * WebSocket 接受信息事件
     * 接收处理客户端发来的数据
     * @param message 信息
     */
    @OnMessage
    public void onMessage(String message) {
        Message msg = JSON.parseObject(message, Message.class);
        sendAllMessage(JSON.toJSONString(msg));
    }

    /**
     * WebSocket 错误事件
     * @param session 用户 Session
     * @param error 错误信息
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

        后端OnOpen将新加入的成员的username加入sessionMap中、OnClose将退出的用户从sessionMap中移除。OnMessage中的方法如下:

/**
     * 发送消息到所有用户中
     * @param message 消息
     */
    private void sendAllMessage(String message) {
        try {
            for (Session session : sessionMap.values()) {
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

前端从message中取出消息并展示

聊天界面如下所示:

 实验总结

        在这个实验中,我成功地搭建了一个简单而高效的多人聊天系统,前端采用Vue框架,后端使用Java实现WebSocket通信。这个实验不仅帮助我深入了解了前后端协同工作的原理,还锻炼了我的编程和系统设计能力。

        首先,在前端方面,Vue的使用使得界面的构建变得简单而灵活。Vue的组件化开发模式使得前端代码更加清晰,易于维护。通过Vue的双向数据绑定,我能够实时更新聊天界面,使用户能够及时看到其他用户的消息。Vue的生命周期钩子函数也为页面的初始化和销毁提供了便捷的方式,确保页面的稳定运行。

        在后端方面,Java WebSocket的运用让我更好地理解了实时通信的实现原理。通过使用Java API for WebSocket,我能够轻松创建WebSocket端点,实现与前端的双向通信。通过WebSocket的持久连接,服务器可以将消息实时推送到所有连接的客户端,从而实现多人实时聊天。

        在整个实验过程中,我学到了许多关于前端和后端协同工作的经验。前端和后端的分离使得系统更容易扩展和维护。同时,通过WebSocket的使用,我也深刻理解了实时通信在现代Web应用中的重要性。

代码仓库

后端:GitHub - TOUKAJ/chat-server

前端:GitHub - TOUKAJ/chat-ui 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值