Setup 是 Vue 3 响应式 API 的 metod data component 的封装
<script lang="ts">//这是Vue 2的语法
export default {
name : "person",
setup(){
let name = "114514";
let age = 18;
let tel = "1380000";
function changeName(){
name = "114514";
}
function showName(){
alert(name);
}
function addAge(){
console.log(age);
age++;
}
return { name , age , showName , addAge }
}
}
</script>返回值
通常 setup 的返回值是其中的各个属性及方法 但是 setup 也可以返回一个渲染函数,例如:
return a()=>'114514'此时 <template> 标签下的内容不会被渲染,而是渲染这个渲染函数返回的内容
简化 return
- 将 setup 写在另一个 setup script 标签中:
<script lang="ts" setup>
let name = "114514";
let age = 18;
let tel = "1380000";
function changeName(){
name = "114514";
}
function showName(){
alert(name);
}
function addAge(){
console.log(age);
age++;
}
</script>上面这种方法需要两个 script 标签完成,通过引入 vite-plugin-vue-setup-extend 模块,可以在一个 script 中同时定义组件名和 setup 声明:
<script lang="ts" name="person" setup>
let name = "114514";
let age = 18;
let tel = "1380000";
function changeName(){
name = "114514";
}
function showName(){
alert(name);
}
function addAge(){
console.log(age);
age++;
}
</script>将这个包添加到开发环境而非生产环境:
npm install vite-plugin-vue-setup-extend -D修改 vite.config.ts,启用模块:
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import SetupExtend from 'vite-plugin-vue-setup-extend' //导入插件
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
SetupExtend() //调用插件
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})- 也可以直接:
import { defineOptions } from 'vue'
defineOptions({ name: 'persossn111' })- Vue 3.2 原生支持了这个写法,所以其实啥都不用干
与 Vue 2 的兼容性
属性可以不被封装在 setup 容器中,依然可以被 template 调用 虽然 setup 中不能用 this,但是之外的内容是可以用 this 访问到 setup 中的数据的(因为 setup 的生命周期比 data 早,此时数据已经被创建,所以可以被读取到)
响应式数据创建
首先导入 vue 模块中的 ref
import { ref } from "vue";其后,响应式数据用 ref 包装:
let name = ref("张三");此时是将 name 包装成 RefImpL 的实例对象: 
Ref 响应式对象
ref 的实质其实是对 reactive 对象的又一次封装(响应式包装器),这样 _value 就可以是对象或者是基础类型: 
创建基本类型数据
修改要修改对象的 value 属性, 插值表达式会自动引用 value 属性的值,不需要加 .value
<template>
<div class="person">
<h2>name: {{name}}</h2>
<h2>age: {{age}}</h2>
<button @click="changeName">修改姓名</button>
<button @click="addAge">增加年龄</button>
</div>
</template>function changeName(){
//name.value = "李四";
name.value = "李四";
}不能在修改的时候用一个新的 ref 对象实例去覆盖原有对象,前端响应元素和对象是相互绑定的(在 setup 第一次创建 ref 实例时),如果覆盖对象会失去绑定导致元素不刷新。 开发过程中不肯呢个全是 .value 来一个个修改,使用 Object.assign() 将传入对象的值追加/覆盖到源对象中:
Object.assign(game_list.value[0], {name : 'dota2', price: 100})直接在@click 隐式调用方法中修改属性
<button @click="car.price += 10">+</button>创建响应式对象
参数是对象时,修改数据要先 .value
function change_first_name(){
game_list.value[0].name = 'lol'
}特例
如果 ref 是定义在 reactive 下的,那么解包的时候不需要加 .value(因为在读取 reactive 对象下的 ref 属性的时候,会被自动解包加上 .value)。
Reactive 对象类型响应式数据

ref 接受的是一个标量值,当然它也可以接受一个对象,但 Vue 不会追踪并更新其中的属性变化,reactive 创建的对象中所有属性都是响应式的。 reactive 操作的是原始对象的 proxy,只有操作这个代理对象才具有响应式。
ref 和 reactive 的选择
使用这个设置可以自动在 ref 属性修改的时候加上 .value
- ref 的数据具有深层响应性,可使用 shallowref 来放弃深层响应
- reactive 本身具有响应式,而 ref 是其中的数据具有响应式
- reactive 只适用于对象类型,不适用于原始类型 因此,使用 ref 处理基本数据类型,而使用 reactive 处理对象和数组。


toRefs 与 toRef
如果对 car 的属性进行单独的解构赋值,结果不是响应式的。
let {price,name} = toRefs(car.value)此时 toRefs 返回的是一个对象,其内容是对 car 中每个属性的响应式引用(也就是说改变这个值也会改变原本对象的值)。
至于 toRef,则是返回源对象中单个属性的响应式引用。
const state = reactive({ foo: 'bar' })
const fooRef = toRef(state, 'foo')要使用这俩,需要从 vue 中导入这两个函数。
ref 的本质
vue 的 ref 实质上是在当对象的属性发生变化的时候自动更新相应的 DOM,但是原生 JS 并不能做到这一点。ref 实际上是为数据对象提供了 setter,getter 函数,当调用 setter 更新其值时,触发 DOM 更新,在使用 getter 时,更新其数据依赖。
// 伪代码,不是真正的实现
const myRef = {
_value: 0,
get value() {
track()
return this._value
},
set value(newValue) {
this._value = newValue
trigger()
}
}v-bind 由数据单向绑定 DOM
<input type="text" :value="lastName"><br>单向绑定就是数据修改会引起 DOM 更新,但 DOM 更新不会引起数据更改v-bind:value 可以简写为 :value
v-model 双向绑定
将输入框中的内容和 text 变量的内容绑定:
<input v-model="text">也可以绑定非文本内容,比如一个对象。
<template>
<select v-model="x">
<option :value="{number:123}">A</option>
</select>
</template>
<script setup lang='ts' name='test'>
import { ref, reactive } from 'vue'
let x = ref(123)
</script>底层实现
<input :value="text" @input="event => text = event.target.value">input 拥有一个属性 value,用于控制显示内容,这里将显示的内容和 text 变量单向绑定(text-->value),然后在有输入事件的时候将新输入的数据传给 text 以实现表单数据的双向绑定。 输入法 需要输入法的语言在拼字阶段不会触发 v-model 的更新,如果此时要求实时更新的话需要通过底层实现的形式完成绑定。 html 原生组件 在原生组件上 v-model 会默认绑定属性,这时候不要手动指定绑定的属性名称。
<input type="checkbox" id="checkbox" v-model="checked" />此外,还支持 select,select multiple 等(最好提供一个禁用的空值选项 <option disabled value="">Please select one</option>) 复选框 和 v-model 搭配使用的还有两个属性,可以定义不同状态时绑定回去的文本值。
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />单选按钮
<template>
<input type="radio" v-model="pick" :value="first" />
<input type="radio" v-model="pick" :value="second" />
</template>
<script setup lang='ts' name='test'>
import { ref, reactive } from 'vue'
const pick = ref('')
const first = ref('first')
const second = ref('second')
</script>注意这里的 value 属性一定要绑定到响应式对象上去,不然一次绑定后就会失去响应式。 多选项目绑定 ref 这里选中的数据的 value 会被绑定到 checkedNames 中的项中,不需要显式声明绑定标签属性的原因同上。
<template>
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames" />
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
<label for="mike">Mike</label>
</template>
<script setup lang='ts' name='test'>
import { ref, reactive } from 'vue'
const checkedNames = ref([])
</script>修饰符
. lazy只有当焦点从输入框移开后才更新.number只有当输入内容是数字的时候才会更新.trim默认自动去除用户输入内容中两端的空格
异步 DOM 更新
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// 现在 DOM 已经更新了
}