Skip to content

vue基础

运行项目

安装好依赖之后便可以运行项目了,使用以下命令启动项目

powershell
npm run dev

执行完此命令之后,终端会显示出如下信息

powershell
  VITE v5.2.11  ready in 441 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

这里面的http://localhost:5173/就是我们当前项目开发环境的预览地址了,我们使用浏览器打开,可以看到如下页面

ok!我们现在已经成功的运行了项目,我们目前的所看到的页面是创建项目时脚手架内置的一些代码生成的,这些代码用于演示如何使用vue的特定功能。可以帮助我们快速上手,了解vue的基本用法和最佳实践,不过对于初学者来说不是那么好上手。如果感兴趣的话,大家课后可以自己看看了解一下。我们接下来会正式进入实战案例的编写,初学者可以跟着我们的脚步一步一步的去掌握vue的语法

准备工作

src\views目录下面新建一个文件夹class1,再在class1这个文件夹下面新建一个文件index.vue,作为第一节课的展示页面

index.vue里面编写如下代码

vue
<template>
    <!-- template里面写视图相关的代码 也就是我们在浏览器上能看到的东西-->
    <div>
        <h1>第一节课</h1>
    </div>
</template>
<script setup lang="ts">
// script里面写逻辑相关的代码 setup不用管是什么 只需要知道这是一个语法糖就好 固定写到这里就可以
// lang="ts" 代表我们逻辑代码使用typescript编写 我们前期入门会用js做说明 ts相关知识后续再做说明
</script>

<style scoped>
/* style里面写样式 scoped代表这个样式只在当前文件生效 */
</style>

如果我们按照文档前置准备安装好了vscode的插件,将会看到如下报错

这个并不是因为我们写的代码有问题,而是代码编写有很多种风格,我们用官方脚手架创建的项目建议我们的文件命名风格为多个单词并且使用短横线连接起来的,这个没有硬性要求,完全凭个人喜好,我们本次课程不采用这种命名风格,所以我们先来学习一下怎么把这个规则关掉

打开根目录的.eslintrc.cjs文件,添加一个rules

js
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = {
  root: true,
  extends: [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-typescript',
    '@vue/eslint-config-prettier/skip-formatting'
  ],
  parserOptions: {
    ecmaVersion: 'latest'
  },
  rules: {
    // 关闭名称校验
    'vue/multi-word-component-names': 'off'
  }
}

这时候再打开刚才的文件src\views\class1\index.vue,等vscode加载好新配置,报错就会消失了

注意

如果我们以后接手了一个项目进行二次开发,并且项目有自己的代码风格,我们就要按照本来的代码风格去进行开发,不要随便去改配置文件,因为我们这个是新项目所以无所谓,不要本来项目是短横线连接的你非要写驼峰,这样才能更好的统一我们的代码风格

接下来我们修改src\router\index.ts这个文件,让我们可以在浏览器里访问到我们新建的页面

js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (About.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import('../views/AboutView.vue')
    },
    {
      path: '/class1',
      name: 'class1',
      component: () => import('../views/class1/index.vue')
    },
  ]
})

export default router

这时候我们使用浏览器打开http://localhost:5173/class1将会看到如下页面

当前页面除了第一节课这个标题是我们页面写的,其它的都是脚手架自带的代码,我们并不需要这些代码。因为它是全局生效的,所以我们需要注释掉那部分代码

修改src\App.vue的代码

vue
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
// import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <!-- <header>
    <img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />

    <div class="wrapper">
      <HelloWorld msg="You did it!" />

      <nav>
        <RouterLink to="/">Home</RouterLink>
        <RouterLink to="/about">About</RouterLink>
      </nav>
    </div>
  </header> -->

  <RouterView />
</template>

<style scoped>
/* header {
  line-height: 1.5;
  max-height: 100vh;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

nav {
  width: 100%;
  font-size: 12px;
  text-align: center;
  margin-top: 2rem;
}

nav a.router-link-exact-active {
  color: var(--color-text);
}

nav a.router-link-exact-active:hover {
  background-color: transparent;
}

nav a {
  display: inline-block;
  padding: 0 1rem;
  border-left: 1px solid var(--color-border);
}

nav a:first-of-type {
  border: 0;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }

  nav {
    text-align: left;
    margin-left: -1rem;
    font-size: 1rem;

    padding: 1rem 0;
    margin-top: 1rem;
  }
} */
</style>

修改src\main.ts

vue
// import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')

此时再观察我们的页面就没有多余的东西了

vue模板语法

Vue使用的模板语法是一种基于HTML的声明式语法,用于将数据绑定到Vue实例上。Vue的模板语法允许开发者在模板中使用特定的指令和表达式,以实现动态的数据渲染和交互

变量插值

使用双大括号{{ }}将变量包裹起来,将其插入到模板中。例如:{{ message }}

编写以下代码

vue
<template>
    <!-- template里面写视图模板相关的代码 -->
    <div>
        <h1>第一节课</h1>
        <div>你好{{ message }}</div>
    </div>
</template>
<script setup lang="ts">
// script里面写逻辑相关的代码 setup不用管是什么 只需要知道这是一个语法糖就好 固定写到这里就可以
// lang="ts" 代表我们逻辑代码使用typescript编写
const message = '世界' //定义一个变量叫message 他的值是一个字符串
</script>

<style scoped>
/* style里面写样式 scoped代表这个样式只在当前文件生效 */
</style>

观察页面,可以发现我们已经将变量插入到视图模板中了

表达式插值

在模板中可以使用简单的js表达式进行计算和逻辑操作

编写以下代码

vue
<template>
    <!-- template里面写视图模板相关的代码 -->
    <div>
        <h1>第一节课</h1>
        <div>你好{{ message }}</div>
        <div>数量加一:{{ count + 1 }}</div>
        <div>三元运算:{{ bool ? '真' : '假' }}</div>
    </div>
</template>
<script setup lang="ts">
// script里面写逻辑相关的代码 setup不用管是什么 只需要知道这是一个语法糖就好 固定写到这里就可以
// lang="ts" 代表我们逻辑代码使用typescript编写
const message = '世界'; //定义一个变量叫message 他的值是一个字符串
const count = 1;
const bool = true;
</script>

<style scoped>
/* style里面写样式 scoped代表这个样式只在当前文件生效 */
</style>

仅支持js表达式​

js表达式也就是一段能够被求值的 JavaScript 代码。有一个简单的判断方法就是是否可以合法地写在 return 后面。

js
return count+1;

return bool ? '真' : '假' ;

因此,下面的例子都是无效的:

js
<!-- 这是一个语句,而非表达式 -->
{{ var a = 1 }}

<!-- 条件控制也不支持,请使用三元表达式 -->
{{ if (ok) { return message } }}

指令

Vue提供了一些内置的指令,以特殊的前缀(v-)来表示。指令用于在模板中执行特定的操作,如条件渲染、循环迭代、事件绑定等。

  • 条件渲染:使用v-if指令根据条件来渲染元素。
vue
<div v-if="isShow">显示内容</div>
  • 列表渲染:使用v-for指令来遍历数组或对象,并渲染重复的元素.
vue
<ul>
  <li v-for="item in items">{{ item }}</li>
</ul>
  • 事件绑定:使用v-on指令来绑定事件处理函数。
vue
<button v-on:click="handleClick">点击</button>

提示

有关于指令这部分的内容后面的课程会有相应的讲解,这里只做了解

响应式变量

在前面的章节,我们已经介绍过响应式系统了:数据发生变化时,系统能够自动地检测到这个变化,并更新相关的视图。

修改我们现在已有的代码,我们加入按钮,点击这个按钮去修改我们刚才使用文本插值插进来的变量,看看视图有没有变化

vue
<template>
    <div>
        <h1>第一节课</h1>
        <div>你好{{ message }}</div>
        <button @click="message = '我的世界'">点我改变message</button>
    </div>
</template>
<script setup lang="ts">
let message = '世界';
</script>

<style scoped></style>

再次回到页面,我们会发现点击按钮之后页面并没有发生任何变化,这是因为我们定义的message这个变量并没有用到vue的响应式API,而是用js的写法定义的变量。所以这里也就没有触发响应式系统,vue3最常用的响应式API有两种:分别是refreactive

js定义变量

提示

我们本次课程涉及到的js相关知识,都是基于ECMAScript 6.0(简称 ES6)标准的写法

ES6于2015 年 6 月正式发布,它的目标是使 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言

ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,

而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。所以我们平时所说的用ES6语法,有时也是泛指“下一代 JavaScript 语言”。

es6发布之前,js通常使用var来定义变量,es6发布之后,我们不再推荐使用var进行变量的定义,推荐使用letconst

let

只需要用let关键字后面跟着变量名即可声明一个变量

js
// 字符串
let msg = 'Hello World!';//单引号双引号均可

// 数值
let count = 1;

// 布尔值
let isVip = false;

// 对象
let userInfo = {
  id: 1,
  name: '李雷',
};
// 数值数组
let uids = [1, 2, 3];

// 字符串数组
let names = ['a', 'b', 'c'];

// 对象数组
let memberList = [
  {
    id: 1,
    name: '李雷',
  },
  {
    id: 2,
    name: '韩梅梅',
  },
];

java8同学的特别提示

对于java8来说,声明变量的时候往往需要加上变量的类型,例如

java
// 字符串
String  msg = "Hello World!";

// 数值(整型)
int count = 1;

js是不需要写变量类型的,变量的类型可以根据赋值自动推断,而且js声明变量值的引号也不是必须用英文双引号,英文单引号和双引号均可

不存在变量提升

var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

js
// var 的情况 var已经没有必要学习了 这个例子只是为了纠正之前会用var的同学的写法
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

// 正确的写法:先定义再使用
let bar = 2;
console.log(bar); // 输出为2
不允许重复声明

let不允许在相同作用域内,重复声明同一个变量

js
{
  let a = 10;
  let a = 1;
}

const

const声明一个只读的常量。一旦声明,常量的值就不能改变,它和let的写法一样,只是let可以重新赋值

js
let a=1;
a=2;
console.log(a);//输出为2

const b=1;
b=2;// 报错Assignment to constant variable.
console.log(b);
const拓展

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于引用类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。

js
const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
console.log(foo.prop) // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。

ref

ref是Vue 3中引入的一个函数,用于在Vue组件中创建一个响应式的数据引用。它接收一个初始值作为参数,并返回一个包含响应式数据的引用对象。

下面我们来看一下怎么用ref去定义一个响应式变量

基本类型定义

js
// 字符串
const msg = ref('Hello World!')

// 数值
const count = ref(1)

// 布尔值
const isVip = ref(false)

引用类型定义

js
// 对象
const userInfo = ref({
  id: 1,
  name: '李雷',
})
// 数值数组
const uids = ref([1, 2, 3])

// 字符串数组
const names = ref(['a', 'b', 'c'])

// 对象数组
const memberList = ref([
  {
    id: 1,
    name: '李雷',
  },
  {
    id: 2,
    name: '韩梅梅',
  },
])

掌握了ref怎么定义响应式变量之后,我们修改之前的代码

vue
<template>
    <div>
        <h1>第一节课</h1>
        <div>你好{{ message }}</div>
        <button @click="message = '我的世界'">点我改变message</button>
    </div>
</template>
<script setup lang="ts">
import { ref } from 'vue';

const message = ref('世界');
</script>

<style scoped></style>

再次回到页面点击按钮,观察页面变化

ref的读取与赋值

我们可以通过.value属性来访问和修改数据的值

变量读取

平时对于js普通变量的值,读取的时候都是直接调用其变量名即可:

js
// 读取一个字符串
const msg = 'Hello World!'
console.log(msg)

// 读取一个数组
const uids = [1, 2, 3]
console.log(uids[1])

但是 ref 定义变量的读取,必须通过 .value

WARNING

通过 ref 声明的变量会全部变成对象,不管定义的是什么类型的值,都会转化为一个 Ref 对象,其中 Ref 对象具有指向内部值的单个 Property .value

也就是说,任何 Ref 对象的值都必须通过 xxx.value 才可以正确获取。

但是在template里面去读取或赋值的时候不用写.value,只有在script里面才需要写

这个比较反直觉,大家一定要注意

js
// 读取一个字符串
const msg = ref('Hello World!')
console.log(msg.value)

// 读取一个数组
const uids = ref([1, 2, 3])
console.log(uids.value[1])
变量赋值

我们刚才讲过js变量需要使用 let 声明才可以修改其值,const定义的是无法修改的,但是由于 Ref 对象是个引用类型,所以可以使用 const 声明,直接通过 .value 修改。就像在const拓展里介绍的可以修改const定义的foo的属性一样

js
// 普通变量
let msg = 'Hello World!'

msg="Hello"

// ref变量
const msg = ref('Hello World')

msg.value = 'Hello!'

reactive

reactive 是我们以后会用到的另一个最常用的响应式 API 了,相对于 ref ,它的局限性在于只适合对象、数组和如 MapSet 这样的集合类型

它与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性:

js
// 定义对象
const obj = reactive({ message: 'Hello Vue 3!'})

//定义数组
const arr = reactive([1,2,3])

修改我们的代码:

vue
<template>
    <div>
        <h1>第一节课</h1>
        <div>你好{{ message }}</div>
        <div>reactive你好{{ state.message }}</div>
        <button @click="message = '我的世界'">点我改变message</button>
        <button @click="state.message = '我的世界'">点我改变state的message</button>
    </div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';

const message = ref('世界');
const state = reactive({ message: 'Hello Vue 3!' })
</script>

<style scoped></style>

可以看到数据变化了视图会自动随之变化

reactive的读取与赋值

ref不同,我们可以直接访问和修改reactive对象的属性,而无需使用.value

变量读取
js
import { reactive } from 'vue';

const state = reactive({
  message: 'Hello Vue 3!',
});
// 与普通对象取值无区别
console.log(state.message); // 输出当前的值:Hello Vue 3
变量赋值
js
import { reactive } from 'vue';

const state = reactive({
  message: 'Hello Vue 3!',
});
state.message="你好,vue3"

注意

reactive不可以将整个对象或数组重新赋值,这样会导致该变量失去响应式

以下为一个错误的使用方法

vue
<template>
    <div>
        <h1>第一节课</h1>
        <div>reactive:{{ state.message }}</div>
        <button @click="state = { message: '你好 Vue 3!' }">点我改变state</button>
    </div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';

const state = reactive({ message: 'Hello Vue 3!' })
</script>

<style scoped></style>

如果想将reactive重新赋值而且不丢失响应式,正确的写法如下

js
// 对象
const obj = reactive({ message: 'Hello Vue 3!' })
Object.assign(state, { message: '你好 Vue 3.0!' })

//数组
const arr = reactive([1, 2, 3]);
arr.length = 0;// 在js里代表清空数组
arr.push(4);// 向数组里添加一项

函数调用

在我们之前举的例子里用到了这样的代码

vue
        <button @click="message = '我的世界'">点我改变message</button>

这里面的@click其实是v-on:click的简写,v-on是用来监听 DOM 事件的,并在事件触发时执行对应的 JavaScript代码

等号后面的内容为事件处理器,事件处理器的值可以是:

  1. 内联事件处理器:事件被触发时执行的内联 JavaScript 语句
  2. 方法事件处理器:一个指向组件上定义的方法的属性名或是路径。

内联事件处理器

内联事件处理器通常用于简单场景,例如我们上面的message = '我的世界'

方法事件处理器

随着事件处理器的逻辑变得愈发复杂,内联代码方式变得不够灵活。因此 v-on 也可以接受一个方法(函数)名或对某个方法的调用。

js函数

可以使用关键字function来定义函数,也可以使用箭头函数

js
function add(a, b) {
    return a + b;
}

// 或者使用箭头函数
const add = (a, b) => {
    return a + b;
};

java8同学的特别提示

对于java8来说,函数被称为方法,必须在类中定义,而且参数类型必须是明确定义的例如

java
public class Example {
    public int add(int a, int b) {
        return a + b;
    }
}

js函数是不需要强制写在类里,本身没有严格的publicprivate概念,也不需要写参数类型的,返回类型也可以不声明

在vue3中使用js函数

vue
<template>
    <div>
        <h1>第一节课</h1>
        <div>你好{{ message }}</div>
        <button @click="changeMsg">点我改变message</button>
    </div>
</template>
<script setup lang="ts">
import { ref } from 'vue';

const message = ref('世界');
// 方法定义,普通函数或箭头函数都可以
const changeMsg = () => {
    message.value = '我的世界';
}
</script>

<style scoped></style>

如果参数是传递过来的,只需要加上括号,在对应形参的位置上传递数据即可:

vue
<template>
    <div>
        <h1>第一节课</h1>
        <div>你好{{ message }}</div>
        <button @click="changeMsg('我的世界')">点我改成我的世界</button>
        <button @click="changeMsg('my world')">点我改成my world</button>
    </div>
</template>
<script setup lang="ts">
import { ref } from 'vue';

const message = ref('世界');
const changeMsg = (msg: string) => {
    message.value = msg;
}
</script>

<style scoped></style>

注意

这里在定义函数形参的时候写了类型msg: string,这实际上是ts的相关知识而非js的写法,有关于ts的相关知识我们会在后面的课程中涉及

如果需要取事件自带的参数有两种写法

vue
<template>
    <div>
        <h1>第一节课</h1>
        <button @click="getEvent($event)">使用特殊的 $event 变量</button>
        <button @click="(e) => getEvent(e)">使用内联箭头函数</button>
    </div>
</template>
<script setup lang="ts">

const getEvent = (e: MouseEvent) => {
    console.log(e);
}
</script>

<style scoped></style>