内部系统的资产列表,是个早期用原生 JS 写的老模块:敲关键词表格立刻过滤,点状态按钮只看「在用」或「报废」。功能少时还清爽,需求一叠加就乱——关键词和状态要组合生效、清空保留状态、序号重排,每个回调都手动刷一遍 DOM,改一处漏一处,视图经常跟数据对不上。
我见过不少这类代码,回头看,问题不在写法,在思路:原生 JS 是「命令式」,每一步都得亲自指挥浏览器改页面;而「数据一变、界面跟着变」的需求,本该「声明式」——声明数据长什么样,界面由框架同步。Vue 干的就是这个:数据是唯一真相,模板是它的投影。
这篇按「会用」的标准讲三块核心:模板语法、响应式、组件。每块末尾都链到对应专题,想深入随时点进去。
一、解决什么问题:数据驱动视图
原生 JS 操作页面,套路是「找到 DOM → 改 DOM」:
// 原生:手动操作 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 自动把变化反映到页面:
<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 官方脚手架 create-vue,一条命令初始化(底层 Vite,秒级启动):
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" /> |
<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 的响应式数据两种写法:
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 逻辑里必须 .value。reactive 只对对象生效,基本类型(字符串、数字)只能靠 ref 包一层。
响应式是 Vue 的发动机——「数据变、视图自动更新」靠的就是它。底层是 Proxy 拦截对象读写 + 依赖收集,Vue 2 时代用的是 Object.defineProperty,两者差别很大,值得单独一篇:Vue 响应式原理详解。
五、组件化:把页面拆成积木
一个页面拆成组件:头部导航、筛选栏、资产表格、分页条,各自独立,组合起来。
<!-- 子组件: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><!-- 父组件 -->
<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:逻辑按「功能」组织,而不是按「类型」分散。同一个筛选功能,ref、computed、watch 挨在一起,代码再多也清楚它在干什么:
<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 全对比 |
