最近看了小满的vue3教学视频,官方文档,还有小余同学的笔记
也输出一份自己的总结,方便复习回顾。
Vue3的基本语法
Vue3新特性
1. API 风格
Vue2 是选项API(Options API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期钩子等),导致代码的可读性变差。当需要修改某个逻辑时,需要上下来回跳转文件位置。
Vue3 组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。
Options API
使用选项式 API,我们可以用包含多个选项的对象来描述组件的逻辑,例如 data
、methods
和 mounted
。选项所定义的属性都会暴露在函数内部的 this
上,它会指向当前的组件实例。
<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>
疑问1:为什么@click里面的increment不需要this.increment呢?methods里面的属性不是会暴露到实例上面吗,刚刚试了,@click="this.increment"
组合式 API (Composition API)
在单文件组件中,组合式 API 通常会与 <script setup>
搭配使用。这个 setup
attribute 是一个标识,告诉 Vue 需要在编译时进行一些处理,让我们可以更简洁地使用组合式 API。
<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>
使用ref
包裹之后会变成响应式的数据,我们之后也会介绍
2. 重写双向绑定
3. Vue3优化dom
4 .Vue3 Fragment
5. Vue3 Tree shaking
项目搭建
nvm
nvm安装
我们可以通过nvm来管理我们的node版本,方便切换不同的node版本 但是nvm有些坑,总结如下:
- nvm 路径不能有空格和中文
- 如果你已经有Node版本了,你需要把当前版本收录进去,或者直接卸载掉,用nvm重新安装一遍
- 用1.1.7版本,不要用1.1.9最新版本
- install版本失败,有可能需要给权限,cmd右键管理员启动即可
感谢b站的这位同学
nvm命令
# 查看已经安装的node版本 nvm list # 查看所有的node版本 nvm list available # 安装node nvm install 16.3.2(版本号) # 切换node版本 nvm use 12.17.0
使用vite构建
npm init vue@latest
使用vue脚手架工具构建
使用vue脚手架会自动内置了vue-router
, pinia
,更适合vue的开发
npm init vue@latest
模板语法
文本插值
在script 声明一个变量可以直接在template 使用用法为 {{ 变量名称 }}
<template> <div class="about"> <h1>{{ message }}</h1> </div> </template> <script setup lang="ts"> const message = "文本插值语法"; </script>
模板语法是可以编写条件运算的
<template> <div class="about"> <h1>{{ message.length > 5 ? "一高" : "一快" }}</h1> </div> </template> <script setup lang="ts"> const message = "文本插值语法"; </script>
运算符也是可以支持的
<template> <div class="about"> <h1>{{ message + "运算符" }}}</h1> </div> </template> <script setup lang="ts"> const message = "文本插值语法"; </script>
操作API 也是支持的
<template> <div class="about"> <h1>{{ message.split("").reverse() }}</h1> </div> </template> <script setup lang="ts"> const message = "文本插值语法"; </script>
Attribute 绑定
任何 Vue 指令 (以 v-
开头的特殊 attribute) attribute 的值中
v-text
<span v-text="msg"></span> <!-- 等同于 --> <span>{{msg}}</span>
v-text
通过设置元素的 textContent 属性来工作,因此它将覆盖元素中所有现有的内容。同时设置v-text和标签中的内容会报错
<template> <div v-text="message">同时使用会报错</div> </template> <script setup lang="ts"> const message = "v-text用法"; </script>
v-html
<template> <div v-html="message"></div> </template> <script setup lang="ts"> const message = "<div>innnerHtml</div>"; </script>
v-if
v-if后面的值为真时,才会渲染,当条件改变时会触发过渡效果。
v-if
v-else-if
v-else
v-show
v-show
通过设置内联样式的 display
CSS 属性来工作,当元素可见时将使用初始 display
值。当条件改变时,也会触发过渡效果。但是v-if
会把整个节点变成一个注释节点,v-show的性能会更好一点
<template> <div v-show="bol">v-show为true显示</div> </template> <script setup lang="ts"> const bol = true; </script>
v-on
v-on:
可以简写为@
当用于普通元素,只能监听原生DOM事件
。当用于自定义元素组件,则监听子组件触发的自定义事件
<!-- 方法处理函数 --> <button v-on:click="doThis"></button> <!-- 动态事件 --> <button v-on:[event]="doThis"></button> <!-- 内联声明 --> // $event是一个event对象 <button v-on:click="doThat('hello', $event)"></button> <!-- 缩写 --> <button @click="doThis"></button> <!-- 使用缩写的动态事件 --> <button @[event]="doThis"></button> <!-- 停止传播 --> <button @click.stop="doThis"></button> <!-- 阻止默认事件 --> <button @click.prevent="doThis"></button> <!-- 不带表达式地阻止默认事件 --> <form @submit.prevent></form> <!-- 链式调用修饰符 --> <button @click.stop.prevent="doThis"></button> <!-- 按键用于 keyAlias 修饰符--> <input @keyup.enter="onEnter" /> <!-- 点击事件将最多触发一次 --> <button v-on:click.once="doThis"></button> <!-- 对象语法 --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
v-bind
动态的绑定一个或多个 attribute,也可以是组件的 prop。
缩写::
<!-- 绑定 attribute --> <img v-bind:src="imageSrc" /> <!-- 内联字符串拼接 --> <img :src="'/path/to/images/' + fileName" />
v-model
在表单输入元素或组件上创建双向绑定。期望的绑定值类型:根据表单输入元素或组件输出的值而变化
- 仅限:
<input>
<select>
<textarea>
- components
v-for
<template> <div v-for="item in arr">{{ item }}</div> </template> <script setup lang="ts">
v-once
仅渲染元素的组件一次,并跳过之后的更新
<template> <button @click="increase"></button> <div>{{ val.count }}</div> </template> <script setup lang="ts"> import { ref } from "@vue/reactivity"; const val = ref({ count: 2 }); const increase = () => { console.log('你更新呀'); val.value.count++; }; </script>
当我们点击的时候,数值可以正常的更新,但是加了v-once
之后,仅渲染元素和组件一次,并跳过之后的更新。再点击更新count的值元素不会进行更新
<template> <button @click="increase"></button> <div v-once>{{ val.count }}</div> </template> <script setup lang="ts"> import { ref } from "@vue/reactivity"; const val = ref({ count: 2 }); const increase = () => { console.log('你更新呀'); val.value.count++; }; </script>
v-memo
ref全家桶
ref
Vue 提供了一个 ref()
方法来允许我们创建可以使用任何值类型的响应式 ref
ref接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value
property,指向该内部值。
const count = ref(0) console.log(count) // { value: 0 } console.log(count.value) // 0 count.value++ console.log(count.value) // 1
如果ref是一个文本插值(即一个{{ }}符号)计算的最终值,它也被将解包,不需要再.value
上取值:
{{ object.foo }} {{ object.foo.value }}
ref 被传递给函数或是从一般对象上被解构时,不会丢失响应性:
好像自动解包哪些官方文档还没有看明白
ifRef
判断是不是一个ref对象
import { ref, Ref,isRef } from 'vue' let message: Ref<string | number> = ref("我是message") let notRef:number = 123 const changeMsg = () => { message.value = "change msg" console.log(isRef(message)); //true console.log(isRef(notRef)); //false }
shallowRef
ref()
的浅层作用形式。
const state = shallowRef({ count: 1 }) // 不会触发更改 state.value.count = 2 // 会触发更改 state.value = { count: 2 }
特别注意
- shallowRef只响应到.value,如果这个.value后面在跟值进行修改,就只能在控制台中看见值改变了,而没办法响应到页面中
- 也就是创建一个跟踪自身
.value
变化的 ref,但不会使其值也变成响应式的 - ref不能跟shallowRef一起使用,不然shallowRef会被Ref影响,从而造成视图的更新(失去浅层次响应的作用)
triggerRef()
triggerRef()会强制更新我们收集的依赖
const shallow = shallowRef({ greet: 'Hello, world' }) shallow.value.greet = 'Hello, universe' // 会强制更新 triggerRef(shallow)
customRef()
创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。
customRef()
预期接收一个工厂函数作为参数,这个工厂函数接受 track
和 trigger
两个函数作为参数,并返回一个带有 get
和 set
方法的对象。
一般来说,track()
应该在 get()
方法中调用,而 trigger()
应该在 set()
中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。
<template> <div>customerRef: {{ obj }}</div> <button @click="change"></button> </template> <script setup lang="ts"> import { customRef, ref } from "vue"; const val = ref({ count: 2 }); const obj = MyRef('customerRef') function change() { obj.value = 'customer 修改了' } function MyRef(value) { return customRef((tracker, trigger) => { return { get() { // 收集依赖 tracker(); // 收集完返回出去 return value; }, set(newVal) { // newVal是收到的新的值 value = newVal; // 触发依赖更新 trigger(); }, }; }); } </script>
认识Reactive全家桶
reactive
ref支持所有类型,reactive支持引用类型,Array,Object,Map,Set
ref取值是需要.value的,reactive取值的话不需要.value
reactive 基础用法
import { reactive } from 'vue' let person = reactive({ name:"小满" }) person.name = "大满"
数组异步赋值问题
let person = reactive<number[]>([]) setTimeout(() => { person = [1, 2, 3] console.log(person); },1000)
这样赋值页面是不会变化的因为会脱离响应式
解决方法
使用push方法
import { reactive } from 'vue' let person = reactive<number[]>([]) setTimeout(() => { const arr = [1, 2, 3] person.push(...arr) console.log(person); },1000)
包裹一层对象
type Person = { list?:Array<number> } let person = reactive<Person>({ list:[] }) setTimeout(() => { const arr = [1, 2, 3] person.list = arr; console.log(person); },1000)
shallowReactive
只能对浅层的数据 如果是深层的数据只会改变值 不会改变视图,和shallowRef差不多
认识to全家桶
toRef
toRef只能修改响应式对象的值并且视图发生变化,非响应式的值可以修改,但是视图没有变化
<template> <div> <button @click="change">按钮</button> {{ state }} </div> </template> <script setup lang="ts"> import { reactive, toRef } from "vue"; // 必须加上reactive,才能是响应式,否则toRef没有作用 const obj = reactive({ foo: 1, bar: 1, }); // bar 转化为响应式对象,相当于解构 const state = toRef(obj, "bar"); const change = () => { state.value++; console.log(obj, state); }; </script>
toRefs
可以帮我们批量创建ref对象主要是方便我们解构使用
import { reactive, toRefs } from 'vue' const obj = reactive({ foo: 1, bar: 1 }) let { foo, bar } = toRefs(obj) foo.value++ console.log(foo, bar);
toRaw
将响应式对象转化为普通对象toRaw()
可以返回由 reactive()、readonly() 、shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象。
computed计算属性
计算属性就是当依赖的属性的值发生变化的时候,才会触发它的更改,如果依赖值不变,使用的是缓存中的值
函数形式
import { computed, reactive, ref } from 'vue' let price = ref(0)//$0 let m = computed<string>(()=>{ return `$` + price.value }) price.value = 500
对象形式
<template> <div>{{ mul }}</div> <div @click="mul = 100">click</div> </template> <script setup lang="ts"> import { computed, ref } from 'vue' let price = ref<number | string>(1)//$0 let mul = computed({ get: () => { return price.value }, set: (value) => { price.value = 'set' + value } }) </script> <style> </style>
官方文档案例
<script setup> import { reactive, computed } from 'vue' const author = reactive({ name: 'John Doe', books: [ 'Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery' ] }) // 一个计算属性 ref const publishedBooksMessage = computed(() => { return author.books.length > 0 ? 'Yes' : 'No' }) </script> <template> <p>Has published books:</p> <span>{{ publishedBooksMessage }}</span> </template>
不使用计算属性
<p>Has published books:</p> <span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
如果不使用计算属性的话,需要我们在模板中写很长的一段代码,如果我们在多处地方使用的话,可不希望每个地方都写那么长,所以计算属性的作用就体现出来了
计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建