一. v-model 基本使用
表单提交是开发中非常常见的功能,也是和用户交互的重要手段:
- 比如用户在登录、注册时需要提交账号密码
- 比如用户在检索、创建、更新信息时,需要提交一些数据
这些都要求我们可以在代码逻辑中获取到用户提交的数据,我们通常会使用
v-model指令来完成:v-model指令可以在表单input、textarea以及select元素上创建双向数据绑定- 它会根据控件类型自动选取正确的方法来更新元素
- 尽管有些神奇,但
v-model本质上不过是语法糖,它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理
html<div id="app"> <!-- 1.手动实现双向绑定 --> <input type="text" :value="message" @input="inputChange"> <!-- 2.v-model实现双向绑定 --> <input type="text" v-model="message"> <!-- 3.登录功能 --> <label for="account"> 账号:<input id="account" type="text" v-model="account"> </label> <label for="password"> 密码:<input id="password" type="password" v-model="password"> </label> <h2>{{message}}</h2> </div> <script> const app = Vue.createApp({ data() { return { message: "Hello Model", account: "", password: "" } }, methods: { inputChange(event) { this.message = event.target.value } } }) app.mount("#app") </script>
二. v-model 绑定原理
官方有说到,
v-model的原理其实是背后有两个操作:v-bind绑定value属性的值v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中
html<div id="app"> <!-- 1.手动实现双向绑定 --> <input type="text" :value="message" @input="inputChange"> <!-- 2.v-model实现双向绑定 --> <input type="text" v-model="message"> <h2>{{message}}</h2> </div> <script> const app = Vue.createApp({ data() { return { message: "Hello Model", } }, methods: { inputChange(event) { this.message = event.target.value } } }) app.mount("#app") </script>事实上
v-model更加复杂
三. v-model 绑定各种元素类型
1. v-model 绑定 textarea
我们再来绑定一下其他的表单类型:
textarea、checkbox、radio、select我们来看一下绑定
textarea:vue<div id="app"> <textarea cols="30" rows="10" v-model="content"></textarea> <p>输入的内容: {{content}}</p> </div> <script> const app = Vue.createApp({ data() { return { content: "" } }, }) app.mount("#app") </script>
2. v-model 绑定 checkbox
我们来看一下
v-model绑定checkbox:单个勾选框和多个勾选框单个勾选框:
v-model即为布尔值- 此时
input的value属性并不影响v-model的值
多个复选框:
- 当是多个复选框时,因为可以选中多个,所以对应的
data中属性是一个数组 - 当选中某一个时,就会将
input的value添加到数组中
html<div id="app"> <!-- 1.checkbox单选框: 绑定到属性中的值是一个Boolean --> <label for="agree"> <input id="agree" type="checkbox" v-model="isAgree"> 同意协议 </label> <h2>单选框: {{isAgree}}</h2> <hr <!-- 2.checkbox多选框: 绑定到属性中的值是一个Array --> <!-- 注意: 多选框当中, 必须明确的绑定一个value值 --> <div class="hobbies"> <h2>请选择你的爱好:</h2> <label for="sing"> <input id="sing" type="checkbox" v-model="hobbies" value="sing"> 唱 </label> <label for="jump"> <input id="jump" type="checkbox" v-model="hobbies" value="jump"> 跳 </label> <label for="rap"> <input id="rap" type="checkbox" v-model="hobbies" value="rap"> rap </label> <label for="basketball"> <input id="basketball" type="checkbox" v-model="hobbies" value="basketball"> 篮球 </label> <h2>爱好: {{hobbies}}</h2> </div> </div> <script> const app = Vue.createApp({ data() { return { isAgree: false, hobbies: [] } }, }) app.mount("#app") </script>- 当是多个复选框时,因为可以选中多个,所以对应的
3. v-model 绑定 radio
v-model绑定radio,用于选择其中一项html<div id="app"> <div class="gender"> <label for="male"> <input id="male" type="radio" v-model="gender" value="male"> 男 </label> <label for="female"> <input id="female" type="radio" v-model="gender" value="female"> 女 </label> <h2>性别: {{gender}}</h2> </div> </div> <script> const app = Vue.createApp({ data() { return { gender: "female" } }, }) app.mount("#app") </script>
4. v-model 绑定 select
和
checkbox一样,select也分单选和多选两种情况单选:只能选中一个值
v-model绑定的是一个值- 当我们选中
option中的一个时,会将它对应的value赋值到fruit中
多选:可以选中多个值
v-model绑定的是一个数组- 当选中多个值时,就会将选中的
option对应的value添加到数组fruit中
html<div id="app"> <!-- select的单选 --> <select v-model="fruit"> <option value="apple">苹果</option> <option value="orange">橘子</option> <option value="banana">香蕉</option> </select> <h2>单选: {{fruit}}</h2> <hr> <!-- select的多选 --> <select multiple size="3" v-model="fruits"> <option value="apple">苹果</option> <option value="orange">橘子</option> <option value="banana">香蕉</option> </select> <h2>多选: {{fruits}}</h2> </div> <script> const app = Vue.createApp({ data() { return { fruit: "orange", fruits: [] } }, }) app.mount("#app") </script>
5. v-model的值绑定
目前我们在前面的案例中大部分的值都是在
template中固定好的:- 比如
gender的两个输入框值male、female - 比如
hobbies的三个输入框值basketball、football、tennis
- 比如
在真实开发中,我们的数据可能是来自服务器的,那么我们就可以先将值请求下来,绑定到
data返回的对象中,再通过v-bind来进行值的绑定,这个过程就是值绑定- 这里不再给出具体的做法,因为还是
v-bind的使用过程
html<div id="app"> <!-- 1.select的值绑定 --> <select multiple size="3" v-model="fruits"> <option v-for="item in allFruits" :key="item.value" :value="item.value"> {{item.text}} </option> </select> <h2>多选: {{fruits}}</h2> <!-- 2.checkbox的值绑定 --> <div class="hobbies"> <h2>请选择你的爱好:</h2> <template v-for="item in allHobbies" :key="item.value"> <label :for="item.value"> <input :id="item.value" type="checkbox" v-model="hobbies" :value="item.value"> {{item.text}} </label> </template> <h2>爱好: {{hobbies}}</h2> </div> </div> <script> const app = Vue.createApp({ data() { return { allFruits: [ { value: "apple", text: "苹果" }, { value: "orange", text: "橘子" }, { value: "banana", text: "香蕉" }, ], fruits: [], allHobbies: [ { value: "sing", text: "唱" }, { value: "jump", text: "跳" }, { value: "rap", text: "rap" }, { value: "basketball", text: "篮球" } ], hobbies: [] } } }) app.mount("#app") </script>- 这里不再给出具体的做法,因为还是
四. v-mode 的修饰符
1. 修饰符 - lazy
lazy修饰符是什么作用呢?默认情况下,
v-model在进行双向绑定时,绑定的是input事件,那么会在每次内容输入后就将最新的值和绑定的属性进行同步如果我们在
v-model后跟上lazy修饰符,那么会将绑定的事件切换为change事件,只有在提交时(比如回车)才会触发vue<!-- lazy: 绑定change事件 --> <input type="text" v-model.lazy="message"> <h2>message: {{message}}</h2>
2. 修饰符 - number
我们先来看一下
v-model绑定后的值是什么类型的:message总是string类型,即使在我们设置type为number也是string类型如果我们希望转换为数字类型,那么可以使用
.number修饰符:vue<!-- type:number, 仍然是string类型 --> <input type="number" v-model="counter2"> <h2>counter2:{{counter2}}-{{typeof counter2}}</h2> <!-- number: 自动将内容转换成数字 --> <input type="text" v-model.number="counter"> <h2>counter:{{counter}}-{{typeof counter}}</h2>
另外,在我们进行逻辑判断时,如果是一个
string类型,在可以转化的情况下会进行隐式转换的:下面的
score在进行判断的过程中会进行隐式转化的jsconst score = '100' if (score > 90) console.log('优秀') // 优秀 console.log(typeof score) // string
3. 修饰符 - trim
如果要自动过滤用户输入的首尾空白字符,可以给
v-model添加trim修饰符:html<!-- trim: 去除收尾的空格 --> <input type="text" v-model.trim="content"> <h2>content: {{content}}</h2>
4. 使用多个修饰符
使用多个修饰符:
html<input type="text" v-model.lazy.trim="content"> <h2>content: {{content}}</h2>
5. v-model 在组件上使用
v-model也可以使用在组件上,Vue2版本和Vue3版本有一些区别- 具体的使用方法,后面讲组件化开发再具体学习
五. Vue 组件化开发思想
1. 人处理问题的方式
人面对复杂问题的处理方式:
任何一个人处理信息的逻辑能力都是有限的
所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容
但是,我们人有一种天生的能力,就是将问题进行拆解
如果将一个复杂的问题,拆分成很多个可以处理的小问题,再将其放在整体当中,你会发现大的问题也会迎刃而解

2. 认识组件化开发
组件化也是类似的思想:
如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展
但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了
如果我们将一个个功能块拆分后,就可以像搭建积木一下来搭建我们的项目

3. 组件化开发
- 现在可以说整个的大前端开发都是组件化的天下
- 无论从三大框架(
Vue、React、Angular),还是跨平台方案的Flutter,甚至是移动端都在转向组件化开发,包括小程序的开发也是采用组件化开发的思想 - 所以,学习组件化最重要的是它的思想,每个框架或者平台可能实现方法不同,但是思想都是一样的
- 无论从三大框架(
- 我们需要通过组件化的思想来思考整个应用程序:
- 我们将一个完整的页面分成很多个组件
- 每个组件都用于实现页面的一个功能块
- 而每一个组件又可以进行细分
- 而组件本身又可以在多个地方进行复用
4. Vue 的组件化
组件化是
Vue、React、Angular的核心思想:前面我们的
createApp函数传入了一个对象App,这个对象其实本质上就是一个组件,也是我们应用程序的根组件组件化提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
任何的应用都会被抽象成一颗组件树

接下来,我们来学习一下在
Vue中如何注册一个组件,以及之后如何使用这个注册后的组件
六. 注册 Vue 的全局组件
1. 注册组件的方式
如果我们现在有一部分内容(模板、逻辑等),我们希望将这部分内容抽取到一个独立的组件中去维护,这个时候如何注册一个组件呢?
我们先从简单的开始谈起,比如下面的模板希望抽离到一个单独的组件:
vue<h2>{{title}}</h2> <p>{{message}}</p>注册组件分成两种:
- 全局组件:在任何其他的组件中都可以使用的组件
- 局部组件:只有在注册的组件中才能使用的组件
2. 注册全局组件
我们先来学习一下全局组件的注册:
全局组件需要使用我们全局创建的
app来注册组件通过
component方法传入组件名称、组件对象即可注册一个全局组件了之后,我们可以在
App组件的template中直接使用这个全局组件:
3. 全局组件的逻辑
当然,我们组件本身也可以有自己的代码逻辑:
比如自己的
data、computed、methods等等
4. 组件的名称
在通过
app.component注册一个组件的时候,第一个参数是组件的名称,定义组件名的方式有两种:方式一:使用
kebab-case(短横线分割符)- 当使用
kebab-case定义一个组件时,在引用这个自定义元素时也必须使用kebab-case,例如<my-component-name>
vue<tab-bar></tab-bar> <script> const app = createApp({ //... }) app.component('tab-bar', { //... }) </script>- 当使用
方式二:使用
PascalCase(驼峰标识符)- 当使用
PascalCase定义一个组件时,在引用这个自定义元素时,两种命名法都可以使用 - 也就是说
<my-component-name>和<MyComponentName>都是可接受的
html<tab-bar></tab-bar> <TabBar></TabBar> // 注意: // 1. .Vue文件中上述两种写法都可被解析; // 2. html中是不区分大小写的,<TabBar>也就默认转化为<tabbar>,所以html文件中是不支持第二种写法的 <script> const app = createApp({ //... }) app.component('TabBar', { //... }) </script>- 当使用
七. 注册 Vue 的局部组件
1. 注册局部组件
- 全局组件往往是在应用程序一开始就会全局组件完成,那么就意味着如果某些组件我们并没有用到,也会一起被注册:
- 比如我们注册了三个全局组件:
ComponentA、ComponentB、ComponentC - 在开发中我们只使用了
ComponentA、ComponentB,如果ComponentC没有用到但是我们依然在全局进行了注册,那么就意味着类似于webpack这种打包工具在打包我们的项目时,我们依然会对其进行打包 - 这样最终打包出的
js包就会有关于ComponentC的内容,用户在下载对应的js文件时也会增加包的大小
- 比如我们注册了三个全局组件:
- 所以在开发中我们通常使用组件的时候采用的都是局部注册:
- 局部注册是在我们需要使用到的组件中,通过
components属性选项来进行注册 - 比如之前的
App组件中,我们有data、computed、methods等选项了,事实上还可以有一个components选项 - 该
components选项对应的是一个对象,对象中的键值对是组件的名称: 组件对象
- 局部注册是在我们需要使用到的组件中,通过
2. 局部组件注册代码

八. Vue 的开发模式解析
1. Vue 的开发模式
- 目前我们使用
vue的过程都是在html文件中,通过template编写自己的模板、脚本逻辑、样式等 - 但是随着项目越来越复杂,我们会采用组件化的方式来进行开发:
- 这就意味着每个组件都会有自己的模板、脚本逻辑、样式等
- 当然我们依然可以把它们抽离到单独的
js、css文件中,但是它们还是会分离开来 - 也包括我们的
script是在一个全局的作用域下,很容易出现命名冲突的问题 - 并且我们的代码为了适配一些浏览器,必须使用
ES5的语法 - 在我们编写代码完成之后,依然需要通过工具对代码进行构建、代码
- 所以在真实开发中,我们可以通过一个后缀名为
.vue的single-file components(单文件组件SFC)来解决 - 并且可以使用
webpack或vite或rollup等构建工具来对其进行处理
2. 单文件的特点
在这个组件中我们可以获得非常多的特性:
代码的高亮
ES6、CommonJS的模块化能力组件作用域的
CSS可以使用预处理器来构建更加丰富的组件,比如
TypeScript、Babel、Less、Sass等
3. 如何支持 SFC
- 如果我们想要使用这一方便的
SFC的.vue文件,比较常见的是两种方式:- 方式一:使用
Vue CLI来创建项目,项目会默认帮助我们配置好所有的配置选项,可以在其中直接使用.vue文件 - 方式二:自己使用
webpack或rollup或vite这类打包工具,对其进行打包处理 - 在公司进行开发,通常都会采用
Vue CLI的方式来完成
- 方式一:使用
4. VSCode 对 SFC 文件支持的插件
- 在前面我们提到过,真实开发中多数情况下我们都是使用
SFC(single-file components)单文件组件 - 我们先说一下
VSCode对SFC的支持:- 插件一:
Vetur,从Vue2开始就一直在使用的VSCode支持Vue的插件 - 插件二:
Volar,官方推荐的插件(对Vue3支持更好)
- 插件一:
九. Vue CLI 安装和使用
1. Vue CLI 脚手架
什么是
Vue脚手架?- 我们前面学习了如何通过
webpack配置Vue的开发环境,但是在真实开发中我们不可能每一个项目从头来完成所有的webpack配置,这样导致开发的效率会大大的降低 - 所以在真实开发中,我们通常会使用脚手架来创建一个项目,
Vue的项目我们使用的就是Vue的脚手架 - 脚手架其实是建筑工程中的一个概念,在我们软件工程中也会将一些帮助我们搭建项目的工具称之为脚手架
- 我们前面学习了如何通过
Vue的脚手架就是Vue CLI:CLI是Command-Line Interface,翻译为命令行界面我们可以通过
CLI选择项目的配置 和 创建Vue CLI已经内置了webpack相关的配置,我们不需要从零来配置
2. Vue CLI 安装和使用
安装
Vue CLI(最新版本是v5.0.8,目前已经处于维护状态)我们是进行全局安装,这样在任何时候都可以通过
vue的命令来创建项目shellnpm i @vue/cli -g
升级
Vue CLI:如果是比较旧的版本,可以通过下面的命令来升级
shellnpm update @vue/cli -g
通过
Vue的命令来创建项目shellVue create 项目名称
十. Vue 的项目目录分析
1. vue create 项目的过程

2. 项目的目录结构
jsconfig.json:用来给vscode读取的,从而给我们提供更好的代码提示,早期是没有该文件的,后面有了之后,官方在创建项目时默认添加了该文件,主要就是用来让vscode识别,从而提供更好的代码提示json{ "compilerOptions": { "target": "es5", // 打包出来es5的代码 "module": "esnext", // 代码中使用模块时,使用esModule的模块化 "baseUrl": "./", // 表示的是src相对的是哪个目录下的src,这里表示的是当前目录下的src "moduleResolution": "node", // 模块的查找规则:node的模块查找规则 "paths": { // 可以在paths中配置路径,之后在import文件路径时,使用别名时,可以让vscode提供更好的路径及文件提示,默认vscode是不会有提示的 "@/*": [ "src/*" ], "utils/*": [ "src/utils/*" ] }, "lib": [ // 用到哪些库 "esnext", "dom", // 提供dom相关的提示 "dom.iterable", // 提供一些dom迭代器更好的提示 "scripthost" ] } }vue.config.js:可以修改配置来覆盖Vue cli创建的webpack默认配置jsconst { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, configureWebpack: { resolve: { // 配置路径别名 // @是默认已经配置好的路径别名: 对应的是src路径,即使加了之后,vscode也不会有提示,需要在jsconfig.js文件中配置上面的信息才能有提示 alias: { "utils": "@/utils" } } } })目录如下


3. Vue CLI 的运行原理

4. 不同引入文件的区别
import { createApp } from 'vue' // 不支持template选项
// import { createApp } from 'vue/dist/vue.esm-bundler' // compile代码
import App from './App.vue' // vue-loader: template -> createVNode过程
import "./utils/abc/cba/nba/index"
/**
* 1.jsconfig.json的演练
* 作用: 给VSCode来进行读取, VSCode在读取到其中的内容时, 给我们的代码更加友好的提示
* 2.引入的vue的版本
* 默认vue版本: runtime, vue-loader完成template的编译过程
* vue.esm-bundler: runtime + compile, 对template进行编译
* 3.补充: 单文件Vue style是有自己的作用域
* style -> scoped
* 4.补充: vite创建一个Vue项目
*/
// 元素 -> createVNode: vue中的源码来完成
// compile的代码
// const App = {
// template: `<h2>Hello Vue3 App</h2>`,
// data() {
// return {}
// }
// }
createApp(App).mount('#app')十一. Vite 方式创建 Vue 项目
vite创建项目shellnpm init vue@latest
