Vue.js
vue官方文档 https://cn.vuejs.org/guide/introduction.html
基于vue方法文档的学习笔记,初学时主要记录基础知识,深度学习后希望能加上自己的理解!
更新时间
2024.05.18第一次更新
什么是Vue
一款用于构建用户界面的 (JavaScript )渐进式框架框架
两个功能:
- 声明式渲染:vue的模板语法使得我们可以声明式地描述HTML和JS状态之间的关系
- 响应性:自动跟踪JS状态并响应式地更新DOM
Vue可以使用的场景
- 无需构建步骤,渐进式增强静态的 HTML
- 在任何页面中作为 Web Components 嵌入
- 单页应用 (SPA)
- 全栈 / 服务端渲染 (SSR)
- Jamstack / 静态站点生成 (SSG)
- 开发桌面端、移动端、WebGL,甚至是命令行终端中的界面
单文件组件(SFC——*.vue)
vue的标志性功能
1 2 3 4 5 6 7 8
| <script setup> </script>
<template> </template>
<style scoped scoped> </style>
|
Vue组件的书写风格
选项式API (Option API)
- 用对象来描述组件逻辑
- 对象包括data、methos、mouted等属性,这些属性都是可选式的(我自己的理解,不一定对)
- 选项定义的属性会暴露在函数内部的this上(即可以通过this访问到这个属性),this指向当前组件的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <script> export default { // data() 返回的属性将会成为响应式的状态 // 并且暴露在 `this` 上 data() { return { count: 0 } },
// methods 是一些用来更改状态与触发更新的函数 // 它们可以在模板中作为事件处理器绑定 methods: { increment() { this.count++ } },
// 生命周期钩子会在组件生命周期的各个不同阶段被调用 // 例如这个函数就会在组件挂载完成后被调用 mounted() { console.log(`The initial count is ${this.count}.`) } } </script>
<template> <button @click="increment">Count is: {{ count }}</button> </template>
|
组合式API(Composition API)
- 可以使用导入的API函数描述组件逻辑
- 组合式API与
<script setup>
搭配使用,其中setup
是一个标识,使得我们可以更简洁地使用组合式API(会在编译时做一些处理)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <script setup> import { ref, onMounted } from 'vue'
// 响应式状态 const count = ref(0)
// 用来修改状态、触发更新的函数 function increment() { count.value++ }
// 生命周期钩子 onMounted(() => { console.log(`The initial count is ${count.value}.`) }) </script>
<template> <button @click="increment">Count is: {{ count }}</button> </template>
|
两者之间的异同
- 选项式API基于组件式API
- 选项式有面对对象的思想,对初学者更友好,强制按照选项来组织代码
- 组件式的核心思想是直接在函数作用域内定义响应式状态变量,并从多个函数中得到的状态组合起来处理复杂问题。更自由、灵活,但更难理解(确实,我不太能理解)
互动教程(组件式API+SFC)
响应式变量声明方式
说的明白点,就是动态的数据绑定,在reactive或ref中声明的变量可以响应式地用在html中
reactive()声明
- reactive只适用于对象(包括数组和内置类型,如Map和Set)
- reactive创建的对象时JS Proxy,行为与普通对象一致
1 2 3 4 5 6 7 8
| import { reactive } from 'vue'
const counter = reactive({ count: 0 })
console.log(counter.count) counter.count++
|
ref()
- ref接收任意类型数据
- 返回值是一个对象,可以通过对象.value属性访问数据
1 2 3 4 5 6
| import { ref } from 'vue'
const message = ref('Hello World!')
console.log(message.value) message.value = 'Changed'
|
在模板template中使用响应式状态
响应式状态暂时我喜欢理解为响应式变量
- 使用
{{}}
使用,并且ref中的对象的value可以不用message.value去访问,而是可以使用message直接访问(因为会被自动解包)
{{}}
中不限制于变量名,也可以是表达式
1 2 3 4 5 6
| //变量写法 <h1>{{ message }}</h1> <p>Count is: {{ counter.count }}</p>
//表达式写法 <h1>{{ message.split('').reverse().join('') }}</h1>
|
Attribute绑定(v-bind)
Attribute n.属性,特质,在编程中通常用来描述数据对象的特征
v-bind用于绑定一个动态值,时v-开头的一种特殊Attribute
绑定的值可以是calss,可以是id,也可以是一些参数
可以简写为:
1 2 3 4 5
| <div v-bind:id="dynamicId"></div>
//语法糖 <div :id="dynamicId"></div>
|
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script setup> import { ref } from 'vue'
const titleClass = ref('title') </script>
<template> <h1 :class="titleClass">Make me red</h1> </template>
<style> .title { color: red; } </style>
|
事件监听(v-on)
使用v-on监听DOM事件
简写为@
1 2 3 4 5 6
| <template> <button v-on:click="increment">{{ count }}</button> //简写 <button @click="increment">{{ count }}</button>
</template>
|
在script中声明回调函数increment
1 2 3 4 5 6 7 8 9 10
| <script setup> import { ref } from 'vue'
const count = ref(0)
function increment() { // 更新组件状态 count.value++ } </script>
|
表单的双向绑定
使用v-bind+v-on
当v-on监听到表单内容的变化,就使用回调函数获取到表单的新内容,更新数据后,重新响应在v-bind绑定的组件上
使用v-model(常用于表单、单选、多选、下拉框)
- v-model是实质是上述方法的语法糖
- V-model将绑定的值与input中的值自动同步,不需要在使用事件处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| //用法 <input v-model="text">
//例子 <script setup> import { ref } from 'vue'
const text = ref('')
</script>
<template> <input v-model="text" placeholder="Type here"> <p>{{ text }}</p> </template>
|
条件渲染(v-if)
v-if
,只有在awesome为true
时,h1
标签才会被渲染
v-else-if
和v-else
用法与JS中if-else if-else
的用法基本一致
1 2 3 4 5 6
| //v-if <h1 v-if="awesome">Vue is awesome!</h1>
//v-else <h1 v-if="awesome">Vue is awesome!</h1> <h1 v-else>Oh no 😢</h1>
|
列表渲染(v-for)
1 2 3 4 5 6 7 8 9 10 11
| <ul> <li v-for="todo in todos" :key="todo.id"> {{ todo.text }} </li> </ul>
//在数组中新增数据 todos.value.push(newTodo)
//在数组中删除数据,过滤数据 todos.value = todos.value.filter(/* ... */)
|
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <script setup> import { ref } from 'vue'
// 给每个 todo 对象一个唯一的 id let id = 0
const newTodo = ref('') const todos = ref([ { id: id++, text: 'Learn HTML' }, { id: id++, text: 'Learn JavaScript' }, { id: id++, text: 'Learn Vue' } ])
function addTodo() { // 新增todo if(newTodo.value != ''){ console.log(newTodo.value); todos.value.push({ id: id++, text: newTodo.value }); } newTodo.value = ''//添加完数据后要重置为空 }
function removeTodo(todo) { // 删除todo todos.value = todos.value.filter((item)=>{ return item!=todo; }); } </script>
<template> <form @submit.prevent="addTodo"> <input v-model="newTodo" required placeholder="new todo"> <button>Add Todo</button> </form> <ul> <li v-for="todo in todos" :key="todo.id"> {{ todo.text }} <button @click="removeTodo(todo)">X</button> </li> </ul> </template>
|
计算属性(computed)
- 创建一个计算属性 ref,这个 ref 会动态地根据其他响应式数据源来计算其
.value
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { ref, computed } from 'vue'
const hideCompleted = ref(false) const todos = ref([ /* ... */ ])
const filteredTodos = computed(() => { // 根据 `todos.value` & `hideCompleted.value` // 返回过滤后的 todo 项目 })
- <li v-for="todo in todos"> + <li v-for="todo in filteredTodos">
|
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <script setup> import { ref, computed } from 'vue'
let id = 0
const newTodo = ref('') const hideCompleted = ref(false) const todos = ref([ { id: id++, text: 'Learn HTML', done: true }, { id: id++, text: 'Learn JavaScript', done: true }, { id: id++, text: 'Learn Vue', done: false } ])
//当hideCompleted为true的时候,应该隐藏掉todos中done属性为ture的属性,所以过滤时返回done为false的属性的对象 const filteredTodos = computed(() => { return hideCompleted.value ? todos.value.filter((t) => !t.done) : todos.value })
function addTodo() { todos.value.push({ id: id++, text: newTodo.value, done: false }) newTodo.value = '' }
function removeTodo(todo) { todos.value = todos.value.filter((t) => t !== todo) } </script>
<template> <form @submit.prevent="addTodo"> <input v-model="newTodo" required placeholder="new todo"> <button>Add Todo</button> </form> <ul> <li v-for="todo in filteredTodos" :key="todo.id"> <input type="checkbox" v-model="todo.done"> <span :class="{ done: todo.done }">{{ todo.text }}</span> <button @click="removeTodo(todo)">X</button> </li> </ul> <button @click="hideCompleted = !hideCompleted"> {{ hideCompleted ? 'Show all' : 'Hide completed' }} </button> </template>
<style> .done { text-decoration: line-through; } </style>
|
生命周期和模板引用
- 当我们需要手动操作DOM时,会需要使用
模板引用
,也就是指向模板中的一个DOM元素的ref
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <p ref="pElementRef">hello</p> </template>
<script setup> //使用同名ref访问该引用 const pElementRef = ref(null)
//要在挂载之后执行代码,我们可以使用 onMounted() 函数: import { onMounted } from 'vue' //ref引用的是一个DOM元素,在这个例子中,就是一个p标签 onMounted(() => { // 此时组件已经挂载。 pElementRef.value.textContent = "你好" }) </script>
|
此时,这个 ref 使用 null 值来初始化。这是因为当<script setup> 执行时,DOM 元素还不存在。模板引用 ref 只能在组件挂载后访问。
onMounted
被称为生命周期钩子——它允许我们注册一个在组件的特定生命周期调用的回调函数。还有一些其他的钩子如 onUpdated
和 onUnmounted
。
侦听器(watch)
有时我们需要响应性地执行一些“副作用”——例如,当一个数字改变时将其输出到控制台。我们可以通过侦听器来实现它:
1 2 3 4 5 6 7 8
| import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newCount) => { // 没错,console.log() 是一个副作用 console.log(`new count is: ${newCount}`) })
|