Skip to content

内部系统的资产列表,是个早期用原生 JS 写的老模块:敲关键词表格立刻过滤,点状态按钮只看「在用」或「报废」。功能少时还清爽,需求一叠加就乱——关键词和状态要组合生效、清空保留状态、序号重排,每个回调都手动刷一遍 DOM,改一处漏一处,视图经常跟数据对不上。

我见过不少这类代码,回头看,问题不在写法,在思路:原生 JS 是「命令式」,每一步都得亲自指挥浏览器改页面;而「数据一变、界面跟着变」的需求,本该「声明式」——声明数据长什么样,界面由框架同步。Vue 干的就是这个:数据是唯一真相,模板是它的投影

这篇按「会用」的标准讲三块核心:模板语法、响应式、组件。每块末尾都链到对应专题,想深入随时点进去。

一、解决什么问题:数据驱动视图

原生 JS 操作页面,套路是「找到 DOM → 改 DOM」:

javascript
// 原生:手动操作 DOM
const input = document.querySelector('#keyword')
input.addEventListener('input', () => {
  const keyword = input.value
  document.querySelectorAll('.asset-row').forEach((row) => {
    row.style.display = row.textContent.includes(keyword) ? '' : 'none'
  })
})

代码越多,DOM 操作越乱:哪里改了数据,就要记得在哪里刷新视图,漏一处就出现「数据变了页面没动」的 bug。

Vue 换个思路:你只管数据,视图由框架同步。声明一个响应式数据,页面模板里引用它,数据一变,Vue 自动把变化反映到页面:

html
<script setup>
import { ref, computed } from 'vue'

const keyword = ref('')                    // 响应式数据
const assets = ref(['笔记本', '显示器', '服务器', '打印机'])

// 计算属性:依赖 keyword 和 assets,任一变化自动重算
const filtered = computed(() =>
  assets.value.filter((name) => name.includes(keyword.value))
)
</script>

<template>
  <input v-model="keyword" placeholder="输入关键词" />
  <ul>
    <li v-for="name in filtered" :key="name">{{ name }}</li>
  </ul>
</template>

v-model 把输入框和 keyword 绑起来,v-for 循环渲染列表,computed 自动算过滤结果。整个过程没有一行手动 DOM 操作。

Vue 数据驱动视图工作流

二、起步:三行命令建项目

Vue 官方脚手架 create-vue,一条命令初始化(底层 Vite,秒级启动):

bash
npm create vue@latest my-app
cd my-app
npm install
npm run dev

也可以不用脚手架,直接 <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> 引一个文件开干,适合改造老页面。下面所有例子都以脚手架项目为准。

三、模板语法:三分钟上手

模板就是 HTML 加上少量指令。最常用的五个:

指令作用例子
{{ name }}文本插值{{ count }}
v-bind(简写 :绑定属性<img :src="url" />
v-if / v-else条件渲染<p v-if="ok">有</p>
v-for列表渲染{{ item.name }} 循环渲染,:key 必给
v-model表单双向绑定<input v-model="keyword" />
html
<template>
  <!-- 插值 + 属性绑定 -->
  <p>{{ user.name }},你有 {{ unread }} 条未读</p>
  <img :src="user.avatar" alt="头像" />

  <!-- 条件渲染 -->
  <span v-if="unread > 0" class="badge">{{ unread }}</span>
  <span v-else>没有新消息</span>

  <!-- 列表渲染:key 必须给,Vue 靠它复用 DOM -->
  <ul>
    <li v-for="asset in assets" :key="asset.id">{{ asset.name }}</li>
  </ul>

  <!-- 双向绑定:输入框内容变化 → keyword 同步变 -->
  <input v-model="keyword" />
</template>

几个要点:

  • v-for:key 一定要给,给 id 而不是索引——Vue 用它判断哪些节点可以复用,索引做 key 在列表增删时会出状态错乱。
  • v-model 是语法糖,等价于「:value + @input」两条指令,后面组件通信篇会拆开看。
  • {{ }} 只能用在模板文本里,属性上要用 : 绑定。

四、响应式:ref 和 reactive

Vue 3 的响应式数据两种写法:

javascript
import { ref, reactive } from 'vue'

// 基本类型用 ref:访问/修改要 .value
const count = ref(0)
count.value++

// 对象用 reactive:直接点属性
const user = reactive({ name: 'BinMaker', age: 30 })
user.age = 31

模板里 ref 会自动解包,写 {{ count }} 而不是 {{ count.value }};但在 JavaScript 逻辑里必须 .valuereactive 只对对象生效,基本类型(字符串、数字)只能靠 ref 包一层。

响应式是 Vue 的发动机——「数据变、视图自动更新」靠的就是它。底层是 Proxy 拦截对象读写 + 依赖收集,Vue 2 时代用的是 Object.defineProperty,两者差别很大,值得单独一篇:Vue 响应式原理详解

五、组件化:把页面拆成积木

一个页面拆成组件:头部导航、筛选栏、资产表格、分页条,各自独立,组合起来。

html
<!-- 子组件:AssetCard.vue -->
<script setup>
// 定义 props:父组件传进来的数据
const props = defineProps({
  name: String,
  price: Number,
})
// 定义事件:子组件要通知父组件时抛出去
const emit = defineEmits(['select'])

const handleClick = () => emit('select', props.name)
</script>

<template>
  <div class="card" @click="handleClick">
    <h3>{{ name }}</h3>
    <p>¥{{ price }}</p>
  </div>
</template>
html
<!-- 父组件 -->
<script setup>
import AssetCard from './AssetCard.vue'

const handleSelect = (name) => console.log('选中了', name)
</script>

<template>
  <!-- 父传子:props;子传父:@select 监听事件 -->
  <AssetCard name="ThinkPad X1" :price="8999" @select="handleSelect" />
</template>

原则只有一句话:父组件通过 props 往下传数据,子组件通过 emit 往上抛事件,数据单向流动,好排查。传参细节、跨层级传值、全局状态管理,都收在组件通信全景

六、Composition API:<script setup> 里的组织方式

上面例子全用的 <script setup>,这就是 Vue 3 主推的组合式 API:逻辑按「功能」组织,而不是按「类型」分散。同一个筛选功能,refcomputedwatch 挨在一起,代码再多也清楚它在干什么:

html
<script setup>
import { ref, computed, watch } from 'vue'

// 一个功能的全部逻辑聚在一起
const keyword = ref('')
const assets = ref([])
const filtered = computed(() => assets.value.filter((a) => a.name.includes(keyword.value)))
watch(keyword, () => console.log('关键词变了:', keyword.value))
</script>

Vue 2 时代的 Options API(data/methods/watch 分区块)还能用,但新项目默认组合式。两者对比、为什么演进、setup 的执行时机,看Composition API 详解

小结

知识点一句话深入
数据驱动视图只改数据,视图自动同步响应式原理
模板语法插值 + 5 个常用指令快速入门即够
响应式ref 包基本类型,reactive 包对象响应式原理详解
组件props 向下传,emit 向上抛组件通信全景
组织代码<script setup> 按功能聚合Composition API 详解
版本差异本文基于 Vue 3版本演进:Vue 2 与 Vue 3 全对比