Skip to content

一. v-model 基本使用


  • 表单提交是开发中非常常见的功能,也是和用户交互的重要手段:

    • 比如用户在登录、注册时需要提交账号密码
    • 比如用户在检索、创建、更新信息时,需要提交一些数据
  • 这些都要求我们可以在代码逻辑中获取到用户提交的数据,我们通常会使用 v-model 指令来完成:

    • v-model 指令可以在表单 inputtextarea 以及 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 更加复杂

    image-20220730215239016

三. v-model 绑定各种元素类型


1. v-model 绑定 textarea

  • 我们再来绑定一下其他的表单类型:textareacheckboxradioselect

  • 我们来看一下绑定 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 即为布尔值
    • 此时 inputvalue 属性并不影响 v-model 的值
  • 多个复选框:

    • 当是多个复选框时,因为可以选中多个,所以对应的 data 中属性是一个数组
    • 当选中某一个时,就会将 inputvalue 添加到数组中
    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 的两个输入框值 malefemale
    • 比如 hobbies 的三个输入框值 basketballfootballtennis
  • 在真实开发中,我们的数据可能是来自服务器的,那么我们就可以先将值请求下来,绑定到 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 类型,即使在我们设置 typenumber 也是 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 在进行判断的过程中会进行隐式转化的

      js
      const 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. 人处理问题的方式

  • 人面对复杂问题的处理方式:

    • 任何一个人处理信息的逻辑能力都是有限的

    • 所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容

    • 但是,我们人有一种天生的能力,就是将问题进行拆解

    • 如果将一个复杂的问题,拆分成很多个可以处理的小问题,再将其放在整体当中,你会发现大的问题也会迎刃而解

      image-20220731173641161

2. 认识组件化开发

  • 组件化也是类似的思想:

    • 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展

    • 但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了

    • 如果我们将一个个功能块拆分后,就可以像搭建积木一下来搭建我们的项目

      image-20220731173715083

3. 组件化开发

  • 现在可以说整个的大前端开发都是组件化的天下
    • 无论从三大框架(VueReactAngular),还是跨平台方案的 Flutter,甚至是移动端都在转向组件化开发,包括小程序的开发也是采用组件化开发的思想
    • 所以,学习组件化最重要的是它的思想,每个框架或者平台可能实现方法不同,但是思想都是一样的
  • 我们需要通过组件化的思想来思考整个应用程序:
    • 我们将一个完整的页面分成很多个组件
    • 每个组件都用于实现页面的一个功能块
    • 而每一个组件又可以进行细分
    • 而组件本身又可以在多个地方进行复用

4. Vue 的组件化

  • 组件化是 VueReactAngular 的核心思想:

    • 前面我们的 createApp 函数传入了一个对象 App,这个对象其实本质上就是一个组件,也是我们应用程序的根组件

    • 组件化提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用

    • 任何的应用都会被抽象成一颗组件树

      image-20220731173848438
  • 接下来,我们来学习一下在 Vue 中如何注册一个组件,以及之后如何使用这个注册后的组件

六. 注册 Vue 的全局组件


1. 注册组件的方式

  • 如果我们现在有一部分内容(模板、逻辑等),我们希望将这部分内容抽取到一个独立的组件中去维护,这个时候如何注册一个组件呢?

  • 我们先从简单的开始谈起,比如下面的模板希望抽离到一个单独的组件:

    vue
    <h2>{{title}}</h2>
    <p>{{message}}</p>
  • 注册组件分成两种

    • 全局组件:在任何其他的组件中都可以使用的组件
    • 局部组件:只有在注册的组件中才能使用的组件

2. 注册全局组件

  • 我们先来学习一下全局组件的注册:

    • 全局组件需要使用我们全局创建的 app 来注册组件

    • 通过 component 方法传入组件名称、组件对象即可注册一个全局组件了

    • 之后,我们可以在 App 组件的 template 中直接使用这个全局组件:

      image-20220731174106223

3. 全局组件的逻辑

  • 当然,我们组件本身也可以有自己的代码逻辑:

    • 比如自己的 datacomputedmethods 等等

      image-20220731174140285

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. 注册局部组件

  • 全局组件往往是在应用程序一开始就会全局组件完成,那么就意味着如果某些组件我们并没有用到,也会一起被注册:
    • 比如我们注册了三个全局组件:ComponentAComponentBComponentC
    • 在开发中我们只使用了 ComponentAComponentB,如果 ComponentC 没有用到但是我们依然在全局进行了注册,那么就意味着类似于 webpack 这种打包工具在打包我们的项目时,我们依然会对其进行打包
    • 这样最终打包出的 js 包就会有关于 ComponentC 的内容,用户在下载对应的 js 文件时也会增加包的大小
  • 所以在开发中我们通常使用组件的时候采用的都是局部注册:
    • 局部注册是在我们需要使用到的组件中,通过 components 属性选项来进行注册
    • 比如之前的 App 组件中,我们有 datacomputedmethods 等选项了,事实上还可以有一个 components 选项
    • components 选项对应的是一个对象,对象中的键值对是 组件的名称: 组件对象

2. 局部组件注册代码

image-20220731175132683

八. Vue 的开发模式解析


1. Vue 的开发模式

  • 目前我们使用 vue 的过程都是在 html 文件中,通过 template 编写自己的模板、脚本逻辑、样式等
  • 但是随着项目越来越复杂,我们会采用组件化的方式来进行开发:
    • 这就意味着每个组件都会有自己的模板、脚本逻辑、样式等
    • 当然我们依然可以把它们抽离到单独的 jscss 文件中,但是它们还是会分离开来
    • 也包括我们的 script 是在一个全局的作用域下,很容易出现命名冲突的问题
    • 并且我们的代码为了适配一些浏览器,必须使用 ES5 的语法
    • 在我们编写代码完成之后,依然需要通过工具对代码进行构建、代码
  • 所以在真实开发中,我们可以通过一个后缀名为 .vuesingle-file components (单文件组件 SFC)来解决
  • 并且可以使用 webpackviterollup 等构建工具来对其进行处理

2. 单文件的特点

  • 在这个组件中我们可以获得非常多的特性:

    • 代码的高亮

    • ES6CommonJS 的模块化能力

    • 组件作用域的 CSS

    • 可以使用预处理器来构建更加丰富的组件,比如 TypeScriptBabelLessSass

      image-20220731175357474

3. 如何支持 SFC

  • 如果我们想要使用这一方便的 SFC.vue 文件,比较常见的是两种方式:
    • 方式一:使用 Vue CLI 来创建项目,项目会默认帮助我们配置好所有的配置选项,可以在其中直接使用 .vue 文件
    • 方式二:自己使用 webpackrollupvite 这类打包工具,对其进行打包处理
    • 在公司进行开发,通常都会采用 Vue CLI 的方式来完成

4. VSCode 对 SFC 文件支持的插件

  • 在前面我们提到过,真实开发中多数情况下我们都是使用 SFC single-file components )单文件组件
  • 我们先说一下 VSCodeSFC 的支持:
    • 插件一:Vetur,从 Vue2 开始就一直在使用的 VSCode 支持 Vue 的插件
    • 插件二:Volar,官方推荐的插件(对 Vue3 支持更好)

九. Vue CLI 安装和使用


1. Vue CLI 脚手架

  • 什么是 Vue 脚手架?

    • 我们前面学习了如何通过 webpack 配置 Vue 的开发环境,但是在真实开发中我们不可能每一个项目从头来完成所有的 webpack 配置,这样导致开发的效率会大大的降低
    • 所以在真实开发中,我们通常会使用脚手架来创建一个项目,Vue 的项目我们使用的就是 Vue 的脚手架
    • 脚手架其实是建筑工程中的一个概念,在我们软件工程中也会将一些帮助我们搭建项目的工具称之为脚手架
  • Vue 的脚手架就是 Vue CLI

    • CLICommand-Line Interface,翻译为命令行界面

    • 我们可以通过 CLI 选择项目的配置 和 创建

    • Vue CLI 已经内置了 webpack 相关的配置,我们不需要从零来配置

      image-20220731180010011

2. Vue CLI 安装和使用

  • 安装 Vue CLI(最新版本是 v5.0.8,目前已经处于维护状态)

    • 我们是进行全局安装,这样在任何时候都可以通过 vue 的命令来创建项目

      shell
      npm i @vue/cli -g
  • 升级 Vue CLI

    • 如果是比较旧的版本,可以通过下面的命令来升级

      shell
      npm update @vue/cli -g
  • 通过 Vue 的命令来创建项目

    shell
    Vue create 项目名称

十. Vue 的项目目录分析


1. vue create 项目的过程

image-20220731180231122

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 默认配置

    js
    const { defineConfig } = require('@vue/cli-service')
    
    module.exports = defineConfig({
      transpileDependencies: true,
      configureWebpack: {
        resolve: {
          // 配置路径别名
          // @是默认已经配置好的路径别名: 对应的是src路径,即使加了之后,vscode也不会有提示,需要在jsconfig.js文件中配置上面的信息才能有提示
          alias: {
            "utils": "@/utils" 
          }
        }
      }
    })
  • 目录如下

    image-20220731200230635image-20220731180320955

3. Vue CLI 的运行原理

image-20220731180641938

4. 不同引入文件的区别

js
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 创建项目

    shell
    npm init vue@latest

如有转载或CV请标注本站原文地址