主题
vue介绍与官网(Lesson2 1/3)
本页对应 Lesson02,共 5 段视频,约 1 小时 11 分。
学习目标
- 能说明
key在 Vue 复用重复元素时的作用,并通过为不同分支或列表项设置唯一key,复现并修复切换登录方式后输入框内容残留的问题。 - 能解释
v-model是:value与输入事件结合的语法糖,能分别用v-model和:value + @input实现双向绑定并比较两者写法。 - 能使用
v-model为文本框、下拉框、单选框和多选框设置初始值,并在提交时正确获取表单内容,必要时使用.number将值转为数字。 - 能区分原生事件绑定与 Vue 事件绑定写法,能在模板中正确使用
@click执行语句、调用方法或传递参数,并说明事件处理函数中this的指向。 - 能使用
.prevent修饰符阻止链接跳转、表单提交和右键菜单等默认行为,并能说明其与event.preventDefault()的等价关系。
知识点
本节资料与学习目标
本节笔记未附图片,仅保留网盘地址:https://www.aliyundrive.com/s/2A6w17TNADy
这节笔记标题写作“一、vue介绍”,但按转写稿,实际内容是对 Vue 指令的复习与补充:key 管理重复元素、v-model 语法糖、表单初始值与数据收集、事件绑定注意事项、prevent 修饰符。学习时不要只记语法,要跟着转写稿里的调试过程理解“为什么这样写”。
从挂载开始:Vue 实例与容器
为什么需要:先有容器和实例,后面的指令才有作用范围。
怎么想:页面里准备一个根元素,通常 id 为 app 或 root,再用 new Vue 挂载。
怎么做:
html
<div id="app"></div>
<script>
new Vue({
el: '#app',
data: { loginType: 'email' }
})
</script>易错点:el 写选择器时要和页面 id 一致;配置对象里的 data、methods 拼写要细心。转写稿反复提醒“代码一模一样却没效果”,往往就是节点、选择器或方法名写错。
用 key 管理被复用的元素
为什么需要:转写稿用“邮箱登录/手机登录”切换演示:两个 input 标签相同,Vue 的 diff 会尽量复用元素,只改属性,不重新创建,于是输入框里的值可能被保留。
怎么想:v-if/v-else 决定显示哪一块;当结构相似时,要告诉 Vue “它们不是同一个元素”。
怎么做:用 template 包裹,并给 input 加不同的 key:
html
<template v-if="loginType === 'email'">
<label for="email">邮箱</label>
<input id="email" placeholder="请输入邮箱" :key="'email'">
</template>
<template v-else>
<label for="phone">手机</label>
<input id="phone" placeholder="请输入手机号" :key="'phone'">
</template>
<button @click="toggleLoginType">切换登录方式</button>切换时修改 loginType 即可。转写稿调试发现:不加 key,切换后手机号还在;加 key 后元素按不同身份重新创建。
易错点:key 要稳定且区分;转写稿中还尝试过 v-for 与 template 结合,出现问题后改用 div,说明指令组合要按实际结构调试。
v-model 语法糖:从单向到双向
为什么需要:v-bind 只能把数据渲染到视图,用户在输入框里改内容,数据不会自动变。
怎么想:双向绑定本质是“绑定 value + 监听 input 事件并写回数据”。
怎么做:
html
<input v-model="username">
<!-- 等价理解 -->
<input :value="username" @input="username = $event.target.value">转写稿还提到可写完整形式 v-model:value,一般省略 :value;修饰符可接在后面,如 .lazy、.number、.trim。
易错点:v-model 主要用于表单元素;v-bind 是单向。事件里 $event.target.value 才是输入内容,别把 $event 和 target 弄混。
表单初始值与数据收集
为什么需要:登录、修改页都要“设置初值”和“提交时收集数据”。
怎么想:用 v-model 同时完成初值显示和输入收集;下拉、单选、多选的值常用数字,避免存汉字。
怎么做:
html
<select v-model.number="city">
<option value="1">北京</option>
<option value="2">上海</option>
</select>
<input type="radio" name="gender" value="1" v-model.number="gender">男
<input type="radio" name="gender" value="2" v-model.number="gender">女
<input type="checkbox" value="1" v-model="hobbies">学习js
data: { city: 2, gender: 2, hobbies: [1, 2] }易错点:只写 :value 能设初值,但输入后数据不更新;需要收集就用 v-model。数字值加 .number,否则可能收到字符串。多选绑定数组,初值写数字数组。
事件绑定:原生写法与 Vue 写法
为什么需要:Vue 里既要能调用方法,也要能传参、拿事件对象。
怎么想:原生 onclick 属性值常是字符串;Vue 用 @click,值是函数或语句。
怎么做:
html
<button @click="fn">不传参,默认接收事件对象</button>
<button @click="fn()">调用函数,可手动传参</button>
<button @click="fn($event)">手动传事件对象</button>易错点:不要把 @click 和 :onclick 混用;转写稿演示 :onclick="fn()" 会在渲染求值时调用函数,把结果赋给原生属性,和 @click 不是一回事。方法名、括号、$event 都要写清。
prevent 修饰符:阻止默认行为
为什么需要:链接会跳转、表单会提交刷新、右键会出菜单,这些默认行为常常要阻止。
怎么想:原生用 event.preventDefault();Vue 提供 .prevent 修饰符。
怎么做:
html
<a href="https://www.baidu.com" @click.prevent="fn">跳转至百度</a>
<form @submit.prevent="handleSubmit">
<input v-model="wd" name="wd">
<button>搜索</button>
</form>
<div @contextmenu.prevent>右击不出现菜单</div>易错点:修饰符写在事件名后,如 @click.prevent;表单提交用 @submit.prevent,不要只在按钮上点一下却忘了表单默认提交。转写稿强调 .prevent 使用频率很高,要能一眼看懂它等价于阻止默认行为。
先看官网
本节笔记本身只给了官网入口,课堂转写稿实际围绕 v-if/v-show、key、v-model、表单取值和事件修饰符展开。先把官网放在这里,后面遇到 API 细节可回查:
- 英文官网:http://vuejs.org
- 中文官网:https://cn.vuejs.org
Vue2中文官网:https://v2.cn.vuejs.org
为什么需要:官网是查指令、修饰符和版本差异的第一手资料。怎么想:先确认自己看的是 Vue2 还是 Vue3 文档,避免把不同版本写法混用。怎么做:遇到 v-model、v-on、key 等不确定点时,按当前项目版本回官网核对。易错点:中文站和 Vue2 站不是同一个入口,别把 Vue2 示例直接套到 Vue3 项目里。
用 key 管理被复用的元素
为什么需要:课堂做了一个“邮箱登录/手机登录”切换案例。默认显示邮箱,点击按钮切到手机,再切回邮箱。调试时发现:在手机输入框里输入 15101125517,切回邮箱再切回来,手机号还在。原因不是数据没变,而是 Vue 的 Diff 算法在比较新旧虚拟 DOM 时,认为两个 input 是同类元素,复用了原来的 DOM,只替换了 id、placeholder 等属性,输入框里的 value 没有被重新生成。
怎么想:当 v-if / v-else-if 切换的两块内容结构相似,尤其都是 input 时,要意识到“元素可能被复用”。如果业务要求两块输入框完全独立,就要给它们不同的 key,让 Diff 认为它们不是同一个节点。
怎么做:用 template 包住一组元素,配合 v-if / v-else-if,并给 input 加不同 key:
html
<template v-if="loginType === 'email'">
<label for="email">邮箱</label>
<input id="email" placeholder="请输入邮箱" :key="'email'">
</template>
<template v-else-if="loginType === 'phone'">
<label for="phone">手机</label>
<input id="phone" placeholder="请输入手机号" :key="'phone'">
</template>
<button @click="toggleLoginType">切换登录方式</button>js
data() {
return {
loginType: 'email'
}
},
methods: {
toggleLoginType() {
this.loginType = this.loginType === 'email' ? 'phone' : 'email'
}
}易错点:v-show 和 v-if 不一样。课堂里把 v-if 改成 v-show 后,两个区域会同时存在,只是隐藏/显示,不是替换;而且 v-show 不能直接和 template 结合使用,演示中改成 div 才正常。key 要加在真正需要区分的元素上,不要以为随便加就能解决所有复用问题。
v-model 的语法糖
为什么需要:表单里既要显示初始值,又要让用户输入后数据同步变化。只用 :value 是单向绑定:数据能驱动视图,但视图改数据不会自动回写。
怎么想:v-model 本质上是“:value + @input”的结合体。课堂演示了等价写法:
html
<input :value="userName" @input="userName = $event.target.value">它和下面效果一样:
html
<input v-model="userName">怎么做:普通文本框直接 v-model;需要延迟同步可用 .lazy,需要数字可用 .number。课堂还提到 v-model 可带修饰符,写完整时类似 v-model:value,但一般省略 :value。
易错点:v-model 主要面向表单元素;普通元素用 v-bind 更合适。@input 里 $event.target.value 才是输入框当前内容,不要写成 $event.value。课堂还提醒:模板里直接写 alert(...) 这类内置函数会报错,应在 methods 中封装后再调用。
表单初始值与获取表单内容
为什么需要:登录、修改资料、提交搜索都要“设置初始值”和“收集表单内容”。课堂强调表单重点就两件事:怎么取值、怎么设初值。
怎么想:文本框、下拉、单选、多选都先让 v-model 绑定数据。下拉和单选常存数字编号,而不是汉字,因为数字占空间小、搜索快;从表单收集到的是字符串时,用 .number 转数字。
怎么做:
html
<select v-model.number="city">
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">成都</option>
</select>
<label><input type="radio" name="sex" value="1" v-model.number="sex">男</label>
<label><input type="radio" name="sex" value="2" v-model.number="sex">女</label>
<label><input type="checkbox" v-model="hobby" value="1">学习</label>
<label><input type="checkbox" v-model="hobby" value="2">睡觉</label>js
data() {
return {
city: 2,
sex: 2,
hobby: [1, 3]
}
}易错点:多选 v-model 要绑数组;数组里写数字还是字符串要和 value 保持一致。下拉、单选如果不加 .number,收集到的是字符串,后续比较或提交可能出错。表单提交默认会刷新/跳转,课堂用 @submit.prevent 或按钮事件里 event.preventDefault() 阻止。
事件写法与 .prevent
为什么需要:原生事件里常写 onclick="fn()",但 Vue 模板里事件值可以是函数名、函数调用语句或表达式。写错会“页面一加载就执行”,或者点击没反应。
怎么想:@click="fn" 是把函数交给事件;@click="fn()" 是模板编译时调用函数并把返回值当处理逻辑,容易立刻执行。需要传参时写 @click="fn(参数)",需要事件对象用 $event。
怎么做:阻止默认行为可用 .prevent:
html
<a href="https://www.baidu.com" @click.prevent="fn">跳转至百度</a>
<form @submit.prevent="handleSubmit">
<input v-model="keyword" name="wd">
<button>搜索</button>
</form>易错点:课堂演示了 @click.prevent 可阻止链接跳转、表单提交、右键菜单等默认行为;但不要把所有逻辑都堆在模板表达式里,复杂逻辑放 methods。事件简写 @click 比 v-on:click 更常用,但要知道二者是一回事。
先认识 Vue:渐进式框架与作者
笔记原文保留如下:
Vue (读音 /vjuː/,类似于 view)是一套用于构建用户界面的渐进式框架。
作者:尤雨溪
为什么需要:如果只用原生 DOM 写界面,数据一变就要手动找元素、改内容、处理表单和事件,页面一复杂就容易乱。Vue 的价值在于把“数据”和“视图”关联起来,让开发者更多关注数据与逻辑。
怎么想:“渐进式”可以理解为不必一上来就把整套工程化方案全用上。转写稿里也是从一个根元素、一个 Vue 实例、一个配置对象开始演示:先让 Vue 接管一块界面,再逐步加入指令、表单绑定、事件等能力。
怎么做:转写稿中出现的挂载写法可以整理为:
html
<div id="app"></div>
<script>
new Vue({
el: '#app'
})
</script>这里的 el 是 element 的缩写,用来指定 Vue 接管的容器;转写稿中也提到容器 id 叫 root 或 app 都可以,示例里用的是 #app。
易错点:读音是 /vjuː/,类似 view;el 要写选择器字符串;不要把“渐进式”误解成“只能做小页面”,它强调的是可以按需引入、逐步扩展。
从“数据驱动”理解 Vue 的更新方式
为什么需要:转写稿反复强调“数据驱动”:数据一旦改变,会驱动视图重新执行。如果不理解这一点,就容易直接改输入框里的 value,却奇怪为什么数据没变。
怎么想:Vue 更新视图不是把整块 DOM 全部推倒重来,而是会比较新旧虚拟 DOM,也就是转写稿里说的 diff 算法,尽量找出不同点再更新。
怎么做:在登录方式切换的例子中,转写稿用 loginType 这样的数据控制显示邮箱还是手机。点击按钮时改的是数据,视图随之变化,而不是手动去替换 DOM。
易错点:转写稿里有一个关键调试现象:输入框里输入了内容,再切换登录方式,输入框内容还保留。原因是 diff 认为前后都是 input,复用了元素,只改了部分属性,并没有重新生成输入框。解决思路是给元素加不同的 key,让 Vue 认为它们是不同元素。
条件渲染:v-if、v-show、template 与 key
为什么需要:登录方式、弹层、表单区域经常需要“二选一”显示。用 v-if 或 v-show 可以让显示由数据条件决定。
怎么想:v-if 更偏向条件渲染,v-show 更偏向显示/隐藏。转写稿中还提到 template 只是一个包裹标签,可以和 v-if 结合使用,避免多包一层真实 DOM。
怎么做:转写稿中的思路可整理为:
html
<template v-if="loginType === 'email'">
<label for="email">邮箱</label>
<input id="email" placeholder="请输入邮箱">
</template>
<template v-else>
<label for="phone">手机</label>
<input id="phone" placeholder="请输入手机号">
</template>当出现元素被复用时,可加 key:
html
<input :key="'email'" id="email" placeholder="请输入邮箱">
<input :key="'phone'" id="phone" placeholder="请输入手机号">易错点:转写稿中特别提醒,v-show 不能和 template 结合使用,改成 div 才行;v-if 则可以和 template 结合。还有,key 不要写成相同值,否则仍然会被复用。
v-model 是语法糖:表单初始值与内容收集
为什么需要:表单提交、登录、修改资料都要“设置初始值”和“获取表单内容”。转写稿把这两点列为表单场景的重点。
怎么想:v-model 可以理解为 :value 加输入事件的结合体,是语法糖。它主要用于表单元素,能实现双向绑定;而 v-bind 更偏单向数据绑定,并且可加修饰符。
怎么做:转写稿中的文本框、下拉、单选、多选可整理为:
html
<input v-model="userName">
<select v-model.number="city">
<option value="1">北京</option>
<option value="2">上海</option>
</select>
<input type="radio" value="1" v-model.number="gender">男
<input type="radio" value="2" v-model.number="gender">女
<input type="checkbox" value="1" v-model="hobby">学习
<input type="checkbox" value="2" v-model="hobby">睡觉易错点:只用 :value 设置初始值,输入后数据不会自动变;要收集变化后的内容,用 v-model 更直接。下拉、单选若需要数字,要加 .number,否则拿到的是字符串。多选绑定数组时,v-model 会判断值是否在数组中。转写稿还提到 .lazy、.number、.trim 等修饰符。
事件绑定与 .prevent 修饰符
为什么需要:按钮点击、表单提交、链接跳转、右键菜单都需要事件处理。Vue 中既要能写事件,也要能阻止默认行为。
怎么想:转写稿对比了原生 onclick 和 Vue 的 @click。Vue 里事件值可以写函数名,也可以写语句;@click="fn" 是函数引用,@click="fn()" 是调用。需要事件对象时可用 $event。
怎么做:阻止默认行为可用 .prevent:
html
<a href="https://www.baidu.com" @click.prevent="fn">跳转至百度</a>
<form @submit.prevent="handleSubmit">
<input v-model="keyword">
<button>搜索</button>
</form>转写稿还演示了右键菜单 contextmenu 也能用 .prevent 阻止。
易错点:转写稿中提醒,@click 里不要随意直接调用内置函数,容易报错;更稳妥的是写自定义方法。阻止表单提交、链接跳转、右键菜单时,.prevent 使用频率很高。还要区分“写函数”和“调用函数”的差别,否则可能在渲染阶段就执行了。
MVVM:先理解“数据驱动视图”为什么需要
本节笔记第一条是“遵循 MVVM 模式”。为什么需要:如果像原生 DOM 那样手动找元素、改内容、读表单值,代码会散落在事件回调里,数据和界面容易不同步。MVVM 的目标是把“数据”和“视图”分开:数据变,视图跟着变;视图输入,数据也能被收集。怎么想:把 data 看作唯一数据源,模板负责声明“哪里显示什么”,Vue 负责在数据变化后更新视图。转写稿里反复说“数据驱动”“数据一旦发生改变,会驱动视图重新执行”,并用登录方式切换演示 v-if/v-else:loginType 是数据,邮箱/手机表单是视图,改数据就切换显示。怎么做:先声明配置对象和挂载点,再把模板中的显示与 data 建立绑定。例如转写稿中的思路可简化为:
html
<div id="app">
<template v-if="loginType === 'email'">
<label for="email">邮箱</label>
<input id="email" placeholder="请输入邮箱">
</template>
<template v-else>
<label for="phone">手机</label>
<input id="phone" placeholder="请输入手机号">
</template>
<button @click="toggle">切换登录方式</button>
</div>易错点:不要把 MVVM 理解成“完全不用管 DOM”。转写稿里调试登录切换时发现:v-if 切换后输入框内容还在,原因是 diff 认为前后都是 input,复用了元素;解决是给元素加 key,让 Vue 知道这是不同元素。这里纠正同音字:转写稿里的“低付算法”是 diff 算法,“微干货”是 v-for,“微杠艺术”是 v-if,“微杠S”是 v-else。
编码简洁、体积小、效率高:靠语法糖和 diff 落地
为什么需要:项目里大量是表单、列表、条件显示和事件处理,如果每次都用原生写法,代码冗长且易错。Vue 提供指令和语法糖,让常见操作更短。怎么想:简洁不是“少写几行”而已,而是把重复逻辑交给框架。转写稿里用 v-model 讲语法糖:它本质上是 :value 加输入事件收集值的结合体,所以一个指令就能完成双向绑定。效率高则来自数据变化后的 diff 比较:不是整页重渲染,而是尽量找不同、复用相同节点。怎么做:表单初始值和收集用 v-model:
html
<input v-model="username">
<select v-model.number="city">
<option value="1">北京</option>
<option value="2">上海</option>
</select>事件用 @click、@submit,需要阻止默认行为时用修饰符:
html
<a href="https://www.baidu.com" @click.prevent="fn">跳转至百度</a>
<form @submit.prevent="handleSubmit">...</form>易错点:第一,v-model 主要面向表单元素,转写稿提醒它和 v-bind 不同:v-bind 是单向绑定,v-model 才有表单双向收集的意义;第二,数字选择要加 .number,否则收集到的是字符串;第三,事件里不要直接写 alert 这类内置函数名当处理函数,转写稿调试时点击报错,原因是 Vue 把它当自定义函数查找,应该写成 @click="fn" 后在方法里调用,或用语句形式 @click="alert('...')";第四,v-show 是显示/隐藏,不会销毁重建,和 v-if 的替换逻辑不同,转写稿里用 v-show 时两个表单会同时保留在 DOM 中。
只关注界面,可配合第三方库:边界清楚才好扩展
为什么需要:一个项目不只有界面,还有请求、路由、状态管理等。Vue 本身只关注视图层,边界清楚,才方便按需引入第三方库。怎么想:把 Vue 当“视图层工具”,不要指望它包办所有业务能力;表单收集、事件处理、条件渲染、列表渲染是它擅长的,Ajax、路由、复杂状态可以另配库。转写稿中表单提交、登录方式切换、下拉/单选/多选收集,都是“界面层”的典型任务;收集到数据后再提交给服务端。怎么做:先用 Vue 管理模板和数据,再把请求等逻辑放到方法中。例如提交时收集 this.username、this.city,再调用接口。易错点:不要把“Vue 只关注界面”误解为“不能做项目”,而是“不要把所有职责都塞进 Vue 实例”;另外转写稿里 v-if 与 v-show 混用、key 缺失导致输入框被复用,都是边界和渲染机制没想清楚的表现。记住:数据变视图变,但 diff 会复用节点;需要区分元素时用 key,需要阻止默认行为时用 .prevent。
先看本节笔记:Vue 周边库地图
本节笔记给出的是 Vue 周边库清单:
vue-cli(Vue脚手架)axiosvue-router(路由)vuex(状态管理)element-ui(UI组件库)- ……
为什么需要:学到组件、工程化、请求、页面切换和状态共享时,单靠 Vue 核心不够,需要这些库配合。怎么想:先把它们当成“地图”,知道各自解决什么问题,不急着背 API。怎么做:后续分别学习;本节先结合转写稿复习指令基础。易错点:不要把周边库当成核心语法,也不要在本节提前编造未讲的配置细节。
key 管理被复用的元素
为什么需要:v-if / v-show 切换同类元素时,Vue 的 diff 算法会尽量复用已有 DOM。怎么想:同类 input 标签相同时,框架可能只改属性,不重建元素,导致输入框里的值被保留。怎么做:在转写稿的登录方式切换案例中,给两段结构加不同 key:
html
<div v-if="loginType === 'email'" key="email">
<label for="email">邮箱</label>
<input id="email" placeholder="请输入邮箱">
</div>
<div v-else key="phone">
<label for="phone">手机</label>
<input id="phone" placeholder="请输入手机号">
</div>调试过程:先切换邮箱/手机,发现 placeholder、id 变了,但输入框内容还在;加上不同 key 后,元素被重新创建。易错点:key 要稳定且唯一;v-show 是显示隐藏,不是替换,不能把它和 v-if 的复用问题混为一谈。
v-model 语法糖
为什么需要:表单需要“数据变视图变、视图变数据也变”。怎么想:v-model 本质是 :value 加 input 事件的组合。怎么做:
html
<input v-model="username">
<!-- 等价于 -->
<input :value="username" @input="username = $event.target.value">易错点:v-model 主要面向表单元素;修饰符如 .lazy、.number 要写对;不要把它和普通 :value 单向绑定混淆。
设置表单初始值与获取表单内容
为什么需要:登录、修改页都要“回填初始值”和“提交前收集数据”。怎么想:用 v-model 同时完成回填与收集。怎么做:
html
<form @submit.prevent="handleSubmit">
<input v-model="username">
<select v-model.number="city">
<option value="1">北京</option>
<option value="2">上海</option>
</select>
<input type="radio" name="gender" value="1" v-model.number="gender">男
<input type="checkbox" value="1" v-model="hobbies">学习
<button>提交</button>
</form>调试过程:转写稿中先只用 :value,输入后提交拿到的仍是旧数据;改成 v-model 后数据随输入更新。城市、性别用 .number 可避免拿到字符串。易错点:复选框绑定数组;下拉框的值常存数字而不是汉字;提交表单要阻止默认跳转。
事件处理注意事项
为什么需要:Vue 事件既要能写语句,也要能调方法、传参数。怎么想:@click 后可以写表达式,也可以写函数名。怎么做:
html
<button @click="str += '!'">加叹号</button>
<button @click="fn">调用方法</button>
<button @click="fn($event)">传事件对象</button>易错点:转写稿中演示了在模板里直接调用 alert 会报错,说明模板表达式里调用的函数要能在组件实例上找到;复杂逻辑放 methods。需要事件对象时用 $event,不要误写成 event 或漏传。
修饰符 prevent
为什么需要:链接跳转、表单提交、右键菜单都有默认行为。怎么想:prevent 相当于在事件里调用 event.preventDefault()。怎么做:
html
<a href="https://www.baidu.com" @click.prevent="fn">跳转至百度</a>
<form @submit.prevent="handleSubmit">...</form>
<div @contextmenu.prevent>右击我</div>易错点:prevent 要写在事件修饰符位置;阻止默认行为不等于阻止冒泡,后者用 .stop。转写稿反复强调:这个修饰符使用频率很高,但不要和普通事件处理逻辑混写。
本节要解决什么问题
这一节笔记只做一件事:把 Vue 运行起来。转写稿里反复说“首先把我的 Vue 引用进来”,这里的“VOGS/will”是口误,按 Vue 理解;“开个对象”指 Vue 的配置对象,“EL”指 el,“井号 APP”指 #app。这些说法都说明:环境配置不是背命令,而是先让页面能拿到 Vue,再让 Vue 实例挂到某个容器上。
为什么需要:浏览器本身不认识 Vue 的模板语法和指令,必须先引入 Vue 的脚本,或者通过 NPM 安装后交给构建工具处理。
怎么想:学习阶段优先选“直接引入”或“CDN 引入”,工程化项目再选 NPM。
怎么做:按笔记保留三种方式。
易错点:Vue 2 和 Vue 3 的 CDN 地址不同;开发版和生产版用途不同;NPM 安装后不是直接双击 HTML 就能用,通常还要配合构建工具。
直接引入:把 Vue 下载到本地
为什么需要:本地文件不依赖外网,断网也能练习,版本也可控。
怎么想:先去官网下载 vue.js,再用 script 标签引入本地文件。笔记给的是下载地址,不是完整 HTML。
怎么做:保留笔记中的下载地址:
shell
# 下载地址
https://v2.cn.vuejs.org/v2/guide/installation.html下载后按你保存的路径引入,例如把文件放在项目里的 js 目录,再用 script 指向它;具体路径以实际保存位置为准。
易错点:不要凭记忆写 CDN 地址;本地文件名和路径大小写要一致;Vue 2 页面不要误引 Vue 3 文件。
CDN 引入:开发版与生产版要分清
为什么需要:不想下载文件时,CDN 可以快速做单页练习。
怎么想:开发阶段用带警告和调试信息的版本,上线再用删掉警告的生产版。
怎么做:保留笔记代码:
html
<!-- 开发环境版本,包含完整的警告和调试模式 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 生产环境版本,删除了警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>易错点:开发版体积更大但报错清楚;生产版删除了警告,初学时排错困难。CDN 还受网络影响,课堂练习打不开时先检查网络和地址是否写全。
NPM 安装:面向工程化项目
为什么需要:真实项目通常有模块化、打包、依赖管理需求,不能只靠一个 script。
怎么想:NPM 安装的是包,后续通过构建工具或模块语法使用。
怎么做:保留笔记命令:
shell
# 最新稳定版
$ npm install vue易错点:这条命令默认安装当前稳定版,学习 Vue 2 时要确认版本;安装后需要项目里有 package.json 和后续构建配置,不能把“安装完成”等同于“页面已经能运行”。
引入后的最小验证:根元素、挂载与配置对象
转写稿视频 1 给了很好的调试线索:先引用 Vue,再指定一个元素作为“根元素/容器”,一般 id 叫 root 或 app,然后 new Vue,传入配置对象,用 el: '#app' 挂载。这里要纠正转写稿里的同音字:“VOGS/will”是 Vue,“开个对象”是配置对象,“EL Elements”是 el,“井号 APP”是 #app。
为什么需要:只引入脚本不代表 Vue 已经工作,必须让实例挂到页面容器上。
怎么想:先有容器,再有实例;el 的选择器要和容器 id 对应。
怎么做:按转写稿顺序检查:引入 Vue 脚本 → 写容器 → new Vue({ el: '#app' }) → 放到 HTML 页面中运行。
易错点:#app 写成 app;容器 id 和 el 不一致;脚本顺序放错;页面还没加载容器就执行实例。转写稿后续关于 v-model、事件修饰符的内容属于指令部分,本节只取“先引入 Vue、再挂载实例”的调试线索,不强行并入环境配置。
本节标题虽然包含 JS、Vue、React 操作 DOM,但本次转写内容集中在 Vue 的数据驱动 DOM 操作:key、v-model、表单初始值与内容获取、事件绑定和 prevent 修饰符。React 的 DOM 操作不在本次转写中展开,下面不编造补充。
本节定位:从直接操作 DOM 转向数据驱动 DOM
为什么需要:原生 JS 操作 DOM 往往要先 querySelector 拿到元素,再手动改 value、改属性、加事件;表单一多,代码就散。Vue 的思路是“改数据,视图跟着更新”,但更新时不是整块重绘,而会走 Diff 算法比较新旧虚拟 DOM。理解这一点,才能理解后面的 key 复用、v-model 和事件写法。
怎么想:把“我要改哪个 DOM”换成“我要改哪个数据”。例如切换登录方式,不是手动隐藏两个 input,而是改 loginType;输入框内容不是手动取 DOM.value,而是让 v-model 同步到数据。
怎么做:先准备容器和 Vue 实例:
html
<div id="app"></div>
<script>
new Vue({
el: '#app',
data: {},
methods: {}
})
</script>易错点:不要把 Vue 当成“更方便的 querySelector”。一旦用 v-model、@click,就应该让数据驱动视图;否则容易出现“数据变了但视图没变”或“视图变了但数据没变”。
key 管理重复元素
为什么需要:转写中的登录案例默认显示邮箱,点击按钮切换为手机。两个 label + input 结构相似,Vue 的 Diff 会认为它们标签相同,只更新属性,复用原来的 input。结果就是:输入过的手机号在切换后仍留在输入框里,因为 input 节点没有被替换。
怎么想:Diff 是“找不同”。标签一样、结构相似时,Vue 倾向于复用节点;只有属性不同就改属性。要告诉 Vue“这是两个不同身份的元素”,就用 key。v-show 只是显隐,不替换节点,所以不加 key 也可能保留输入值。
怎么做:
html
<div id="app">
<template v-if="loginType === 'email'">
<label for="email">邮箱:</label>
<input id="email" placeholder="请输入邮箱" key="email">
</template>
<template v-else>
<label for="phone">手机:</label>
<input id="phone" placeholder="请输入手机号" key="phone">
</template>
<button @click="toggle">切换登录方式</button>
</div>
<script>
new Vue({
el: '#app',
data: { loginType: 'email' },
methods: {
toggle() {
this.loginType = this.loginType === 'email' ? 'phone' : 'email'
}
}
})
</script>易错点:key 要唯一、稳定,不要两个分支写同一个 key;template 可以和 v-if 结合使用,但它只是包裹标签,不能和 v-show 混用。
v-model 语法糖
为什么需要:表单需要双向绑定:数据能显示到输入框,输入框内容也能写回数据。手写 :value 加 @input 可以,但重复。
怎么想:v-model 本质是 :value 与 @input 的结合体。转写里说“语法糖”,就是这个意思。它主要针对表单元素,不是所有元素都适合。
怎么做:
html
<input v-model="username">
<!-- 等价于 -->
<input :value="username" @input="username = $event.target.value">js
data: { username: '张三' }修饰符:.lazy 在失焦后更新;.number 把输入转数字;.trim 去首尾空格。
易错点:v-bind:value 只负责显示初始值,用户输入不会自动写回数据;要收集就用 v-model。v-model 不要和 :value 重复写。
设置表单初始值与获取内容
为什么需要:登录、修改资料、搜索都离不开表单:既要给表单设置初始值,又要在提交时收集内容。
怎么想:在 Vue 里,v-model 同时承担两件事:绑定初始值、收集用户输入。下拉、单选、多选要注意值类型。转写强调:城市、性别这类数据入库常用数字,不用汉字,因为数字占用小、搜索快;所以要用 .number 或 :value 保证收集到数字。
怎么做:
html
<form @submit.prevent="submit">
<input v-model.trim="username" placeholder="用户名">
<select v-model.number="city">
<option :value="1">北京</option>
<option :value="2">上海</option>
</select>
<label><input type="radio" :value="1" v-model.number="gender">男</label>
<label><input type="radio" :value="2" v-model.number="gender">女</label>
<label><input type="checkbox" :value="1" v-model="hobbies">学习</label>
<label><input type="checkbox" :value="2" v-model="hobbies">睡觉</label>
<button>提交</button>
</form>js
data: {
username: '张三',
city: 2,
gender: 2,
hobbies: [1, 3]
},
methods: {
submit() {
console.log(this.username, this.city, this.gender, this.hobbies)
}
}易错点:select、radio 不加 .number 可能收集到字符串;checkbox 多选要绑定数组;提交表单要阻止默认跳转,可用 @submit.prevent。
事件注意事项与 prevent 修饰符
为什么需要:Vue 事件和原生事件写法不同。原生常写 onclick="fn()";Vue 用 @click。另外,链接跳转、表单提交、右键菜单都有默认行为,很多业务需要阻止。
怎么想:@click="fn" 传方法引用,事件触发时调用,默认能收到事件对象;@click="fn()" 是内联语句,点击时执行,但不会自动把事件对象传进去,需要写 @click="fn($event)"。.prevent 等价于 event.preventDefault()。
怎么做:
html
<a href="https://www.baidu.com" @click.prevent="show">跳转至百度</a>
<form @submit.prevent="submit">
<input v-model="wd">
<button>搜索</button>
</form>
<div @contextmenu.prevent>右击我</div>js
methods: {
show() { alert('百度') },
submit() { console.log(this.wd) }
}易错点:模板表达式作用域有限,像 alert 这类全局函数直接写在模板里可能报错,建议放到 methods 中调用 window.alert;需要事件对象时用 $event;.prevent 在表单提交、链接跳转、右键菜单中频率很高。
Vue 模板语法与本节定位
Vue.js使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层Vue实例的数据。所有Vue.js的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。在底层的实现上,
Vue将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
为什么需要:直接操作 DOM 写登录切换、表单收集会重复、易漏;模板语法把“数据长什么样”声明出来。怎么想:先写数据,再用指令把数据映射到视图;数据变,视图按 diff 结果最小更新。怎么做:围绕 v-if/v-show/v-for/v-model/v-on 练一个登录切换和表单。易错点:不要以为数据一变就整块重建 DOM,正因 Vue 会复用元素,才需要理解 key。
用 key 管理被复用的元素
为什么需要:转写稿的调试场景是登录方式切换:邮箱输入框和手机输入框用 v-if/v-else-if 二选一,切换后输入过的手机号还留在输入框里。怎么想:Vue 初次渲染只渲染满足条件的一块;切换时 diff 发现上下都是 label + input,标签相同,就复用原元素、只改 id/placeholder 等属性,所以 input 的 value 没被清掉。怎么做:给两个 input 加不同 key,让 Vue 认为不是同一元素,从而重建。
html
<div id="app">
<template v-if="loginType === 'email'">
<label for="email">邮箱</label>
<input id="email" placeholder="请输入邮箱" key="email">
</template>
<template v-else-if="loginType === 'mobile'">
<label for="mobile">手机</label>
<input id="mobile" placeholder="请输入手机号" key="mobile">
</template>
<button @click="toggleLogin">切换登录方式</button>
</div>js
new Vue({
el: '#app',
data: { loginType: 'email' },
methods: {
toggleLogin() {
this.loginType = this.loginType === 'email' ? 'mobile' : 'email'
}
}
})易错点:条件里的字符串要写单引号,如 loginType === 'email';template 只是包裹标签,可与 v-if/v-else-if 结合;转写稿还验证了 v-show 是显示隐藏、不销毁元素,且 v-show 不能和 template 结合使用,需要改成普通 div。key 不要重复。
v-model 是语法糖
为什么需要:表单既要显示初始值,又要收集用户输入。怎么想:v-model 本质是 :value 加 @input 的结合体;它主要面向表单元素,并支持 .lazy/.number/.trim 修饰符。怎么做:
html
<input v-model="username">
<input :value="username" @input="username = $event.target.value">
<input v-model.lazy="username">易错点:只写 :value 是单向绑定,输入后数据不变;$event.target.value 才是输入内容;.lazy 是失去焦点/变更后更新,.number 转数字,.trim 去首尾空格。转写稿强调 v-bind 可用于任意元素,v-model 主要用于表单,二者不要混。
设置表单初始值和收集表单内容
为什么需要:登录、修改页都要“回填初始值”和“提交时取数据”。怎么想:初始值就是 data 的初值,收集就是提交时读 this.xxx;用 v-model 把两者统一。怎么做:
html
<form @submit.prevent="handleSubmit">
<input v-model.trim="username" placeholder="用户名">
<select v-model.number="city">
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">成都</option>
</select>
<label><input type="radio" name="gender" value="1" v-model.number="gender">男</label>
<label><input type="radio" name="gender" value="2" v-model.number="gender">女</label>
<label><input type="checkbox" value="1" v-model.number="hobby">学习</label>
<label><input type="checkbox" value="2" v-model.number="hobby">睡觉</label>
<button type="submit">提交</button>
</form>js
data: {
username: '张三',
city: 2,
gender: 2,
hobby: [1, 2]
},
methods: {
handleSubmit() {
console.log(this.username, this.city, this.gender, this.hobby)
}
}易错点:转写稿中只用 :value 设初值,输入后提交拿到的还是旧数据;select/radio/checkbox 的 value 默认是字符串,需要数字时加 .number;多选框绑定数组;form 默认提交会刷新,用 @submit.prevent。
事件绑定与 prevent 修饰符
为什么需要:点击、提交、右键菜单都有默认行为,不阻止会跳转或刷新。怎么想:原生 onclick 属性小写、值是字符串;Vue 用 v-on 或 @,值可以是语句、函数名、函数调用。怎么做:
html
<a href="https://www.baidu.com" @click.prevent="show">百度</a>
<form @submit.prevent="handleSubmit">...</form>
<div @contextmenu.prevent>右键菜单被阻止</div>
<button @click="fn($event, '参数')">传参</button>易错点:.prevent 等价于 event.preventDefault(),常和表单提交、链接跳转、contextmenu 一起用;需要事件对象时用 $event;不要依赖模板里直接调用内置函数,转写稿中测试会报错,放进 methods 更稳。
课堂画面





代码示例
示例一:用 key 管理被复用的元素(v-if 切换登录方式)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>key 管理可复用元素</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- template 只是包裹标签,可以和 v-if / v-else 结合使用 -->
<template v-if="loginType === 'email'">
<label for="email">邮箱:</label>
<input id="email" type="text" placeholder="请输入邮箱" key="email">
</template>
<template v-else>
<label for="phone">手机:</label>
<input id="phone" type="text" placeholder="请输入手机号" key="phone">
</template>
<button @click="changeLoginType">切换登录方式</button>
<p>当前登录方式:{{ loginType }}</p>
</div>
<script>
new Vue({
el: '#app', // 指定 Vue 的根容器(挂载点)
data: {
loginType: 'email' // 默认邮箱登录
},
methods: {
changeLoginType() {
this.loginType = this.loginType === 'email' ? 'phone' : 'email';
}
}
});
</script>
</body>
</html>关键行与易错点
template只是包裹标签,可以和v-if/v-else结合使用,但不能和v-show结合使用。v-if/v-else必须相邻书写,中间不能插入其它元素。label的for要与input的id对应,点击文字才能聚焦输入框。- 核心坑点:不加
key时,Vue 的 diff 算法认为上下两个都是<input>,是"同一个元素",只会替换id、placeholder等属性,不会重新创建 DOM,所以你在邮箱框里输入的123会残留在手机框里。 - 加上不同的
key(key="email"/key="phone")后,Vue 判断为不同元素,会重新创建,输入内容就不会被带过去。 - 条件表达式里
'email'要加引号,写成loginType === email会把email当成变量,报错找不到。
示例二:v-model 语法糖与修饰符
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>v-model 语法糖</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h3>1. v-model 双向绑定</h3>
<input type="text" v-model="userName">
<p>数据:{{ userName }}</p>
<h3>2. 等价写法(手写 :value + @input)</h3>
<input type="text" :value="userName" @input="userName = $event.target.value">
<h3>3. 只写 :value —— 单向绑定,视图改数据不变</h3>
<input type="text" :value="userName">
<h3>4. 修饰符</h3>
<p>lazy(失去焦点才同步):<input type="text" v-model.lazy="userName"></p>
<p>trim(自动去首尾空格):<input type="text" v-model.trim="userName"></p>
<p>number(转数字):<input type="text" v-model.number="age"></p>
<p>age = {{ age }},类型:{{ typeof age }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
userName: '张三',
age: 18
}
});
</script>
</body>
</html>关键行与易错点
v-model的本质就是:value+@input的结合体,官方称之为语法糖,两种写法效果完全一致。- 第 3 个输入框只写了
:value,属于单向数据绑定:改数据视图会变,但改视图数据不会变。 v-model只能用在表单元素上(input/textarea/select),而v-bind(:value)什么元素都能用,这是二者的核心区别。@input="userName = $event.target.value"中$event是事件对象,target就是当前这个input元素。.lazy让同步时机从input事件改为change事件(失去焦点才更新)。.number是把输入值转成数字,不写则收集到的是字符串。
示例三:设置表单初始值、获取表单内容与 .prevent 修饰符
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>表单初始值与数据收集</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- @submit.prevent 阻止表单默认提交(跳转/刷新) -->
<form @submit.prevent="handleSubmit">
<p>
用户名:
<input type="text" v-model.trim="userName">
</p>
<p>
所在城市:
<select v-model.number="city">
<option :value="1">北京</option>
<option :value="2">上海</option>
<option :value="3">成都</option>
<option :value="4">西安</option>
</select>
</p>
<p>
性别:
<label><input type="radio" name="gender" :value="1" v-model.number="gender">男</label>
<label><input type="radio" name="gender" :value="2" v-model.number="gender">女</label>
</p>
<p>
爱好:
<label><input type="checkbox" :value="1" v-model="hobby">学习</label>
<label><input type="checkbox" :value="2" v-model="hobby">睡觉</label>
<label><input type="checkbox" :value="3" v-model="hobby">吃饭</label>
</p>
<button type="submit">提交</button>
</form>
<pre>{{ { userName: userName, city: city, gender: gender, hobby: hobby } }}</pre>
<hr>
<a href="http://www.baidu.com" @click.prevent="showTip">跳转至百度(不会跳)</a>
<div class="box" @contextmenu.prevent>右击我,不会弹出右键菜单</div>
</div>
<script>
new Vue({
el: '#app',
data: {
userName: '张三', // 初始值
city: 2, // 默认选中上海(数字 2)
gender: 2, // 默认选中女
hobby: [1, 2] // 多选绑定数组,默认选中学习、睡觉
},
methods: {
handleSubmit() {
// 这里收集到的就是最新数据,可以直接发给服务端
console.log(JSON.stringify({
userName: this.userName,
city: this.city,
gender: this.gender,
hobby: this.hobby
}));
},
showTip() {
alert('我被点击了,但没有跳转');
}
}
});
</script>
<style>
.box {
width: 200px;
height: 100px;
background: #f5f5f5;
margin-top: 10px;
line-height: 100px;
text-align: center;
}
</style>
</body>
</html>关键行与易错点
v-model既负责设置初始值,又负责收集数据;如果只写:value,能设置初始值但收集不到用户修改后的内容。- 下拉框、单选框的选项值要用
:value="1"(数字) 而不是value="1"(字符串),否则收集到的是字符串,需要再手动转换。 - 单选框必须加相同的
name属性才能互斥,否则男女可以同时选中。 - 复选框多选时
v-model要绑定数组,Vue 会自动判断每个:value是否在数组中,在就选中。 @submit.prevent等价于在方法里写e.preventDefault(),作用是阻止表单的默认提交行为(默认会跳转到当前页或action指定地址)。- 注意:
@click.prevent中prevent后面不要加括号,它是修饰符不是函数。 - 常见易错:在
@click里写alert('xx')会报错,因为 Vue 把alert当作自定义方法去methods里找;要么在methods中定义方法,要么用window.alert(...)。
常见错误
现象:切换邮箱/手机登录方式后,输入框里的内容仍残留。原因:
v-if/v-else分支结构相似,Vue 复用同类型元素,未强制替换。修法:给两个分支元素加唯一key,如key="email"、key="phone"。现象:提交表单时拿到的是旧值。原因:只写了
:value单向绑定,输入内容没有写回data。修法:改用v-model,或使用:value+@input手动更新数据。现象:
@click="fn()"后拿不到事件对象,或参数变成undefined。原因:模板中写的是表达式,不是函数引用;事件对象未显式传入。修法:需要事件对象时写@click="fn($event)";不需要参数时优先写@click="fn",并确保fn在methods中定义。现象:点击提交按钮或搜索按钮后页面刷新/跳转。原因:表单默认提交行为未阻止。修法:用
@submit.prevent="handleSubmit",或按钮事件加.prevent,如@click.prevent。现象:下拉框、单选、多选收集到的值类型不对,数字变成字符串。原因:表单值默认按字符串处理,
v-model未做类型转换。修法:数字场景使用v-model.number,或给选项使用:value绑定数字;多选初值必须是数组。现象:
v-for渲染的输入框在删除/插入中间项后内容错位。原因:key缺失或使用不稳定的index。修法:使用业务唯一id作为key,不要用数组下标承担业务标识。现象:模板里直接写
@click="alert(...)"不生效或报错。原因:模板表达式作用域不是任意全局函数,内置函数不能直接按自定义方法调用。修法:在methods中封装处理函数,模板只调用实例上的方法。
练习
实现“邮箱登录/手机登录”切换:默认显示邮箱,点击按钮切换;在两个输入框分别输入内容后反复切换,要求输入内容不串。请说明你用什么方式解决复用问题。
实现一个表单,包含用户名、城市下拉、性别单选、爱好多选;要求设置初始值,点击提交后输出一个对象。城市和性别必须是数字类型,爱好必须是数组。
实现一个链接点击后弹出链接文本但不跳转,再实现一个搜索表单提交时阻止默认提交。要求分别使用事件对象和
.prevent修饰符各写一次。不用
v-model,只用:value和@input实现输入框双向绑定;在此基础上增加.lazy和.number的效果对比。用
v-for渲染 4 个输入框并支持删除中间项;先不加key观察现象,再加唯一id作为key,记录两次现象差异。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | key管理重复元素 | 20:24 |
| 2 | v-model语法糖 | 09:51 |
| 3 | 设置表单的初始值以及获取表单内容 | 16:11 |
| 4 | 事件注意事项 | 16:40 |
| 5 | 修饰符prevent | 08:37 |
小结
结构相似的分支元素切换时,优先考虑复用问题;需要彻底替换时用唯一
key。表单“设初值”和“收数据”要同时成立,单向绑定只能设初值,不能可靠收集用户输入。
表单提交、链接跳转、右键菜单等默认行为,优先用
.prevent处理,代码更集中。事件绑定的核心是区分“函数引用”和“表达式调用”;需要事件对象时显式传
$event。下拉、单选、多选的值类型要在数据层明确,数字场景不要依赖默认字符串。
v-for的key应稳定且唯一;用index只适合静态且不重排的列表。模板中不要依赖任意全局函数,业务动作统一收敛到
methods,便于调试和复用。