下面我将分步为你讲解如何实现这个计算器,并提供完整的代码。

用vue实现一个简单的网页计算器
(图片来源网络,侵删)

最终效果预览

这是一个我们最终要实现的效果:


第一步:准备工作

你需要一个 Vue 项目,如果你还没有,可以通过 Vue CLI 或者 Vite 来快速创建一个。

使用 Vite (推荐,速度快):

# 1. 创建一个基于 Vue 的项目
npm create vue@latest
# 2. 进入项目目录
cd your-project-name
# 3. 安装依赖
npm install
# 4. 启动开发服务器
npm run dev

使用 Vue CLI:

用vue实现一个简单的网页计算器
(图片来源网络,侵删)
# 1. 安装 Vue CLI (如果还没安装)
npm install -g @vue/cli
# 2. 创建一个新项目
vue create your-project-name
# 3. 进入项目目录并启动
cd your-project-name
npm run serve

项目创建好后,我们开始修改代码。


第二步:编写代码

我们将主要修改两个文件:

  1. src/App.vue: 这是应用的根组件,我们所有的计算器 UI 和逻辑都将写在这里。
  2. src/main.js: 用于挂载 Vue 应用。

修改 src/App.vue

打开 src/App.vue,将默认的代码替换为下面的完整代码,我会逐一解释每个部分的作用。

<template>
  <div class="calculator">
    <!-- 显示屏 -->
    <div class="display">
      {{ displayValue }}
    </div>
    <!-- 按键区域 -->
    <div class="buttons">
      <!-- 第一行:清除和正负号 -->
      <button @click="clearEntry" class="button span-two">CE</button>
      <button @click="toggleSign" class="button">+/-</button>
      <button @click="percentage" class="button">%</button>
      <!-- 数字和运算符按钮 -->
      <button v-for="btn in buttons" 
              :key="btn.value" 
              @click="onButtonClick(btn.value)"
              :class="['button', btn.class]">
        {{ btn.label }}
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
// --- 数据 ---
// 计算器显示屏的值
const displayValue = ref('0');
// 计算器的内部状态
const firstOperand = ref(null);
const operator = ref(null);
const waitingForSecondOperand = ref(false);
// 按钮配置
const buttons = [
  { label: '7', value: '7', class: '' },
  { label: '8', value: '8', class: '' },
  { label: '9', value: '9', class: '' },
  { label: '÷', value: '/', class: 'operator' },
  { label: '4', value: '4', class: '' },
  { label: '5', value: '5', class: '' },
  { label: '6', value: '6', class: '' },
  { label: '×', value: '*', class: 'operator' },
  { label: '1', value: '1', class: '' },
  { label: '2', value: '2', class: '' },
  { label: '3', value: '3', class: '' },
  { label: '−', value: '-', class: 'operator' },
  { label: '0', value: '0', class: 'zero' },
  { label: '.', value: '.', class: '' },
  { label: '=', value: '=', class: 'operator' },
  { label: '+', value: '+', class: 'operator' },
];
// --- 方法 ---
// 处理所有按钮的点击
const onButtonClick = (value) => {
  // 如果按的是数字或小数点
  if ('0123456789.'.includes(value)) {
    inputNumber(value);
  } 
  // 如果按的是运算符
  else if ('+-*/'.includes(value)) {
    inputOperator(value);
  } 
  // 如果按的是等号
  else if (value === '=') {
    calculate();
  }
};
// 输入数字或小数点
const inputNumber = (num) => {
  if (waitingForSecondOperand) {
    displayValue.value = num;
    waitingForSecondOperand.value = false;
  } else {
    // 如果当前显示是 '0',则替换掉,否则追加
    displayValue.value = displayValue.value === '0' ? num : displayValue.value + num;
  }
};
// 输入运算符
const inputOperator = (nextOperator) => {
  const inputValue = parseFloat(displayValue.value);
  // 如果当前已经有运算符在等待第二个操作数,则更新运算符
  if (operator.value && waitingForSecondOperand.value) {
    operator.value = nextOperator;
    return;
  }
  // 如果是第一次输入运算符
  if (firstOperand.value === null) {
    firstOperand.value = inputValue;
  } 
  // 如果已经有第一个操作数,则进行计算
  else if (operator.value) {
    const result = performCalculation();
    displayValue.value = `${parseFloat(result.toFixed(7))}`;
    firstOperand.value = result;
  }
  waitingForSecondOperand.value = true;
  operator.value = nextOperator;
};
// 执行计算
const calculate = () => {
  const inputValue = parseFloat(displayValue.value);
  if (firstOperand.value === null || operator.value === null) {
    return;
  }
  const result = performCalculation();
  displayValue.value = `${parseFloat(result.toFixed(7))}`;
  firstOperand.value = null;
  operator.value = null;
  waitingForSecondOperand.value = false;
};
// 真正执行计算的核心逻辑
const performCalculation = () => {
  const prev = firstOperand.value;
  const current = parseFloat(displayValue.value);
  if (prev === null || operator.value === null) {
    return current;
  }
  switch (operator.value) {
    case '+':
      return prev + current;
    case '-':
      return prev - current;
    case '*':
      return prev * current;
    case '/':
      if (current === 0) {
        alert('Cannot divide by zero!');
        clearAll();
        return 0;
      }
      return prev / current;
    default:
      return current;
  }
};
// 清除当前输入
const clearEntry = () => {
  displayValue.value = '0';
};
// 清除所有状态
const clearAll = () => {
  displayValue.value = '0';
  firstOperand.value = null;
  operator.value = null;
  waitingForSecondOperand.value = false;
};
// 切换正负号
const toggleSign = () => {
  displayValue.value = (parseFloat(displayValue.value) * -1).toString();
};
// 计算百分比
const percentage = () => {
  displayValue.value = (parseFloat(displayValue.value) / 100).toString();
};
</script>
<style scoped>
/* scoped 表示这些样式只作用于当前组件 */
.calculator {
  width: 320px;
  margin: 50px auto;
  border-radius: 10px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
  font-family: Arial, sans-serif;
  background-color: #333;
  overflow: hidden;
}
.display {
  background-color: #222;
  color: #fff;
  font-size: 2.5em;
  text-align: right;
  padding: 20px;
  word-wrap: break-word;
  word-break: break-all;
  min-height: 60px;
}
.buttons {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 1px;
  background-color: #555;
}
.button {
  background-color: #f0f0f0;
  border: none;
  font-size: 1.5em;
  padding: 20px;
  cursor: pointer;
  transition: background-color 0.2s;
  color: #333;
}
.button:hover {
  background-color: #e0e0e0;
}
.button:active {
  background-color: #d0d0d0;
  transform: scale(0.98);
}
/* 运算符按钮样式 */
.button.operator {
  background-color: #ff9500;
  color: white;
}
.button.operator:hover {
  background-color: #e08600;
}
/* 0按钮占两格 */
.button.zero {
  grid-column: span 2;
}
</style>

修改 src/main.js

为了让我们的计算器居中显示,可以稍微修改一下 main.js,打开 src/main.js如下:

用vue实现一个简单的网页计算器
(图片来源网络,侵删)
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')

第三步:代码逻辑解析

<template> 部分 (视图层)

  1. <div class="display">: 这是计算器的显示屏,我们使用 {{ displayValue }} 来显示 displayValue 这个变量的值,Vue 会自动更新这个文本当 displayValue 改变时。
  2. <div class="buttons">: 这是所有按键的容器。
  3. <button v-for="btn in buttons ...>: 这是一个 v-for 指令,它会遍历我们定义的 buttons 数组,为数组中的每一项都生成一个 <button>
    • @click="onButtonClick(btn.value)": 当按钮被点击时,会触发 onButtonClick 方法,并把按钮的 value 作为参数传递过去。
    • class="['button', btn.class]": 这是一个动态绑定类,每个按钮都有 button 这个基础类,如果按钮配置里有额外的 classoperator),也会被加上。

<script setup> 部分 (逻辑层)

我们使用 Composition API (<script setup>) 的方式来组织代码,这是 Vue 3 推荐的写法。

  1. ref: ref 用于创建一个响应式的数据,当 ref 的值改变时,任何引用了它的模板部分都会自动更新。

    • displayValue: 控制屏幕上显示的数字。
    • firstOperand: 存储第一个操作数。
    • operator: 存储当前选择的运算符(如 , )。
    • waitingForSecondOperand: 一个布尔值,用于判断用户是否已经输入了第一个操作数,现在正在等待输入第二个操作数,这对于连续运算(如 1 + 2 + 3)至关重要。
  2. buttons 数组: 一个对象数组,用于定义所有按钮的标签、值和 CSS 类,这样做可以让我们的代码更整洁、易于维护。

  3. 核心方法:

    • onButtonClick(value): 总入口方法,根据按下的按钮类型(数字、运算符、等号)来调用不同的处理函数。
    • inputNumber(num): 处理数字输入,它会处理“追加数字”和“替换屏幕”两种情况。
    • inputOperator(nextOperator): 处理运算符输入,这是逻辑最复杂的部分之一,它需要处理第一次输入运算符和连续输入运算符的不同情况。
    • calculate(): 当按下 时被调用,它会调用 performCalculation 来得出最终结果,并重置计算器状态。
    • performCalculation(): 真正执行 , , , 计算的核心函数。
    • clearEntry(), clearAll(), toggleSign(), percentage(): 这些是辅助功能,分别对应清除当前输入、清除全部、切换正负号和计算百分比。

<style scoped> 部分 (样式层)

  • scoped: 这个关键字非常重要,它确保这里的 CSS 样式只应用于当前组件 (App.vue),而不会影响到项目中的其他组件,避免了样式污染。
  • display: grid: 我们使用 CSS Grid 布局来轻松地创建一个 4 列的按钮网格。
  • grid-column: span 2: 这个样式应用在 0 按钮上,让它跨越两列,实现我们想要的效果。
  • 其他样式主要是为了美化外观,如颜色、阴影、圆角和悬停效果。

第四步:运行与测试

回到你的终端,确保开发服务器正在运行(npm run devnpm run serve)。

然后在浏览器中打开项目提供的 URL(通常是 http://localhost:5173/http://localhost:8080/),你就能看到并使用这个计算器了!

你可以尝试输入 10 + 5 * 2,看看它是否能正确计算(应该得到 30),这验证了我们的运算符优先级逻辑,也可以测试清除、正负号、百分比等功能。

通过这个项目,你学会了:

  • 如何使用 Vue 的模板来构建 UI。
  • 如何使用 ref 创建和管理响应式数据。
  • 如何使用 v-for 指令来动态渲染列表。
  • 如何使用 @click 事件监听器来处理用户交互。
  • 如何组织一个组件的逻辑和样式。
  • 实现了一个相对复杂的状态管理逻辑(计算器的运算流程)。

希望这个详细的教程对你有帮助!