尚硅谷Vue3学习笔记
编码规范
TypeScript+组合式API+setup语法糖
环境配置
npm create vue@latest
npm i
npm run dev
实例
初始
import { createApp } from 'vue' // 引入createApp函数
import App from './App.vue' // 从App.vue中导入组件,并将其命名为App
createApp(App).mount('#app') // 调用createApp函数,将根组件App作为参数传入,并挂载在名为"app"的DOM节点上
一个.vue文件中应有三种标签:template,script(<script lang="ts">),style
Vite项目中,index.html是项目的入口文件,在项目的最外层。
加载index.html后,Vite解析(<script type="module" src="xxx">)指向的ts/js。
Vue3中是通过createApp函数创建一个应用实例。
核心语法
1.OptionsAPI与CompositionAPI
Vue2的API设计是Options风格的,Vue3的API设计是Composition风格的。
OptionsAPI的弊端
Options类型的API,数据、方法、计算属性等,是分散在:data、methods、computed中的,若想新增或者修改一个需求,就需要分别修改:data、methods、computed,不便于维护和复用。
CompositionAPI的优势
可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。
2.setup
1:setup函数中的this是undefined,不可使用。setup执行优先级高于beforeCreate
2:
<template>
<div class="person">
<h2>name:{{ name }}</h2>
<h2>age:{{ age }}</h2>
<button @click="changeName">change name</button>
<button @click="changeAge">change age</button>
<button @click="showTel">look up the phone</button>
</div>
</template>
<script lang="js">
export default{
name:"Person",
setup(){
// 数据
let name = 'zhangsan' // 不是响应式的
let age = 18
let tel ='12345678901'
// 方法
function changeName(){
name = 'lisi'
}
function changeAge(){
age += 1
}
function showTel(){
alert(tel)
}
return {age,name,tel,changeAge,changeName,showTel}
},
}
</script>
这样子将无法改变name、age
3:setup函数的返回值也可以是渲染函数
return ()=>'haha'
4:data、methods中可以使用this读到setup函数中的数据,反之不行
5:为了解决setup函数中时常存在的问题:已定义数据与函数,但是忘记return。setup中被设计了语法糖。
<script lang="ts">
export default{
name:"Person",
}
</script>
<!-- 或者是 <script lang="ts" setup> -->
<!-- 或者是 <script setup lang="ts" name="Person"> 这个安装vite-plugin-vue-setup-extend插件后,可以更改网页视图中的标签名字 -->
<script setup lang="ts">
// defineOptions({
// name: 'Person'
// })
let a=666
</script>
扩展:上述代码,还需要编写一个不写setup的script标签,去指定组件名字,比较麻烦,我们可以借助vite中的插件简化
-
第一步:
npm i vite-plugin-vue-setup-extend -D -
第二步:
vite.config.ts
import { defineConfig } from 'vite'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
plugins: [ VueSetupExtend() ]
})
3.ref创建:基本类型的响应式数据
作用:定义响应式变量。
语法:import { ref } from 'vue' let xxx = ref(初始值)。
返回值: 一个RefImpl的实例对象,简称ref对象或ref,ref对象的value 属性是响应式的。
注意点:
JS中操作数据需要:xxx.value,但模板中不需要.value,直接使用即可。- 对于
let name = ref('张三')来说,name不是响应式的,name.value是响应式的。
4.reactive创建:对象类型的响应式数据
- 作用:定义一个响应式对象(基本类型不要用它,要用
ref,否则报错) - 语法:
let 响应式对象= reactive(源对象)。 - 返回值:一个
Proxy的实例对象,简称:响应式对象。 - 注意点:
reactive定义的响应式数据是“深层次”的。
let obj = {a:1,b:1}
console.log(obj)
显示:{a:1,b:1}
import { reactive } from 'vue'
let obj1 = reactive({a:1,b:1})
let obj2 = reactive({a:1},{b:2})
console.log(obj1)
console.log(obj2)
显示:
Proxy(Object){a:1,b:1}
Proxy(Array){0:{...},1:{...}}
5.ref创建:对象类型的响应式数据
- 其实
ref接收的数据可以是:基本类型、对象类型。 - 若
ref接收的是对象类型,内部其实也是调用了reactive函数。 - 嵌套后的响应式数据依旧是响应式的
let car = reactive({brand:"Benz",price:100})
car.price += 1
-->
let car = ref({brand:"Benz",price:100})
car.value.price += 1
let games = reactive([
{id:"ad1",name:"LOL"},
{id:"ad2",name:"CSGO"},
{id:"ad3",name:"DOTA"},
])
games[0].name = 'lol'
-->
let games = ref([
{id:"ad1",name:"LOL"},
{id:"ad2",name:"CSGO"},
{id:"ad3",name:"DOTA"},
])
games.value[0].name = 'lol'
6:ref对比reactive
宏观角度看:
- 1.
ref用来定义:基本类型数据、对象类型数据 - 2.
reactive用来定义:对象类型数据
区别:
-
ref创建的变量必须使用.value(可以使用volar插件自动添加.value)
- 2.
reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)。Object.assign(a,b,c...)就是将b,c...依次分配给a
使用原则:
- 1.若需要一个基本类型的响应式数据,必须使用
ref - 2.若需要一个响应式对象,层级不深,
ref、reactive都可以 - 3.若需要一个响应式对象,且层级较深,推荐使用
reactive
let car = ref({brand:"Benz",price:100})
function changeCar(){
car.value = {brand:"Mercades",price:150} //correct
}
-->
let car = reactive({brand:"Benz",price:100})
function changeCar(){
car = {brand:"Mercades",price:150} // wrong
car = reactive({{brand:"Mercades",price:150}}) // wrong
Object.assign(car,{brand:"Mercades",price:150}) // correct
}
这样的原因:ref类型的.value一定是响应式的,所以可以直接全部更改
let sum = ref (0)
sum.value += 1 // correct
sum = ref (1) // wrong
7:toRefs和toRef
- 作用:将一个响应式对象中的每一个属性,转换为
ref对象 - 备注:
toRefs与toRef功能一致,但toRefs可以批量转换
let person = reactive({
name:"zhangsan",
age:18
})
let {name,age} = toRefs(person)
let nl = toRef(person,'age')
function changeName(){
name.value += '~'
}
function changeAge(){
age.value += 1
}
name就相当于&person.name,且为ref类型。必须同名
8:computed
<script lang="ts" setup name="Person">
import { computed , ref } from 'vue'
let lastName = ref("zhang")
let firstName = ref("san")
let fullName = computed({
get(){ // 读取
return lastName.value.slice(0,1).toUpperCase() + lastName.value.slice(1,) + ' ' + firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1,)
},
set(val){ // 修改
let [str1='',str2=''] = val.split(' ')
lastName.value = str1
firstName.value = str2
}
})
function changeFullName(){
fullName.value='Li Si'
}
</script>
1.computed类型具有记忆属性,而function()不具有
2.computed.value是只读的,不可修改。只能通过修改computed中需要计算的值来改变computed
9:watch
- 作用:监视数据的变化(和
Vue2中的watch作用一致) - 特点:
Vue3中的watch只能监视以下四种数据:
ref定义的数据。reactive定义的数据。- 函数返回一个值(
getter函数)。- 一个包含上述内容的数组。
情况一:
监视ref定义的【基本类型】数据:直接写数据名即可,监视的是其value值的改变
<script lang="ts" setup name="Person">
import { ref,watch } from 'vue'
let sum = ref (0)
function addSum(){
sum.value += 1
}
let stopWatch = watch(sum,(newValue)=>{
// 此处命名无需一定是newvalue,默认该括号的的第一、二个值为新、旧的前面 的变量
console.log('newValue:'+newValue)
if( newValue >= 10)
stopWatch() // 执行函数即可停止监视。可以任意名字
}) // 配置对象
//},{once:True} )
</script>
情况二:
监视ref定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。
注意:
- 若修改的是
ref定义的对象中的属性,newValue和oldValue都是新值,因为它们是同一个对象。- 若修改整个
ref定义的对象,newValue是新值,oldValue是旧值,因为不是同一个对象了。- 本质上就是两者是否是对于同一个地址值取值
<script lang="ts" setup name="Person">
import { ref,watch } from 'vue'
let person = ref ({name:"zhangsan",age:18})
function changeName(){
person.value.name += '~'
}
function changeAge(){
person.value.age += 1
}
function changeAll(){
person.value = {name:"lisi",age:20}
}
watch(person,(newv,oldv)=>{
console.log("something changed:",newv," the old:",oldv)
},{deep:true,once:false,immediate:true})
</script>
情况三:
监视reactive定义的【对象类型】数据,且默认开启了深度监视。
<script lang="ts" setup name="Person">
import { reactive,watch } from 'vue'
let person = reactive ({name:"zhangsan",age:18})
function changeName(){
person.name += '~'
}
function changeAge(){
person.age += 1
}
function changeAll(){
Object.assign(person,{name:"lisi",age:20})
// 对于该函数,只有在修改前后存在变化时,才能被监测到改变,ref则不需要
}
watch(person,(newv,oldv)=>{
console.log("something changed:",newv," the old:",oldv)
},{deep:true,once:false,immediate:true})
// deep:false 也不会报错
</script>
情况四:
监视ref或reactive定义的【对象类型】数据中的某个属性,注意点如下:
1.若该属性值不是【对象类型】,需要写成函数形式。
2.若该属性值是依然是【对象类型】,可直接编,也可写成函数,建议写成函数。
结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。
<script lang="ts" setup name="Person">
import { reactive,watch } from 'vue'
let person = reactive({
name:"zhangsan",
age:18,
car:{
c1:"benz",
c2:"BMW"
}
})
function changeName(){
person.name += '~'
}
function changeAge(){
person.age += 1
}
function changeC1(){
person.car.c1 = 'wolksvagen'
}
function changeC2(){
person.car.c2 = 'frella'
}
function changecar(){
person.car = {c1:'byd',c2:'lixiang'}
}
watch(()=>{return person.name},()=>{ // 此处的return可省略
console.log("person1 changed")
})
watch(()=>{return person.car},()=>{
console.log("person2 changed")
},{deep:true})
watch(()=>{return person.car},()=>{
console.log("person3 changed")
})
watch(()=>{return person.car.c1},()=>{
console.log("person4 changed")
})
watch(person.car,()=>{ // 这样写也可以,不过当整个被改变时,
// 就不会显示,所以建议写成函数形式
console.log("person5 changed")
})
</script>
情况五:
监视上述的多个数据
<script lang="ts" setup name="Person">
import { reactive,watch } from 'vue'
let person = reactive({
name:"zhangsan",
age:18,
car:{
c1:"benz",
c2:"BMW"
}
})
function changeName(){
person.name += '~'
}
function changeAge(){
person.age += 1
}
function changeC1(){
person.car.c1 = 'wolksvagen'
}
function changeC2(){
person.car.c2 = 'frella'
}
function changecar(){
person.car = {c1:'byd',c2:'lixiang'}
}
watch([()=>person.car.c1,()=>person.name],()=>{
console.log("person changed")
})
</script>
10. watchEffect
- 官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
watch对比watchEffect
- 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
watch:要明确指出监视的数据watchEffect:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。
-
示例代码:
<template> <div class="person"> <h1>需求:水温达到50℃,或水位达到20cm,则联系服务器</h1> <h2 id="demo">水温:{{temp}}</h2> <h2>水位:{{height}}</h2> <button @click="changePrice">水温+1</button> <button @click="changeSum">水位+10</button> </div> </template> <script lang="ts" setup name="Person"> import {ref,watch,watchEffect} from 'vue' // 数据 let temp = ref(0) let height = ref(0) // 方法 function changePrice(){ temp.value += 10 } function changeSum(){ height.value += 1 } // 用watch实现,需要明确的指出要监视:temp、height watch([temp,height],(value)=>{ // 从value中获取最新的temp值、height值 const [newTemp,newHeight] = value // 室温达到50℃,或水位达到20cm,立刻联系服务器 if(newTemp >= 50 || newHeight >= 20){ console.log('联系服务器') } }) // 用watchEffect实现,不用 const stopWtach = watchEffect(()=>{ // 室温达到50℃,或水位达到20cm,立刻联系服务器 if(temp.value >= 50 || height.value >= 20){ console.log(document.getElementById('demo')?.innerText) console.log('联系服务器') } // 水温达到100,或水位达到50,取消监视 if(temp.value === 100 || height.value === 50){ console.log('清理了') stopWtach() } }) </script>
11.标签的ref属性
作用:用于注册模板引用
- 用在普通
DOM(HTML)标签上,获取的是DOM节点 - 用在组件标签上,获取的是组件实例对象
用在普通DOM标签上:
<div class="person">
<h1 ref="title1">尚硅谷</h1>
<h2 ref="title2">前端</h2>
<h3 ref="title3">Vue</h3>
<input type="text" ref="inpt"> <br><br>
<button @click="showLog">点我打印内容</button>
</div>
</template>
<script lang="ts" setup name="Person">
import {ref} from 'vue'
let title1 = ref()
let title2 = ref()
let title3 = ref()
function showLog(){
// 通过id获取元素
const t1 = document.getElementById('title1')
// 打印内容
console.log((t1 as HTMLElement).innerText)
console.log((<HTMLElement>t1).innerText)
console.log(t1?.innerText)
/************************************/
// 通过ref获取元素
console.log(title1.value)
console.log(title2.value)
console.log(title3.value)
}
</script>
用在组件标签上:
<!-- 父组件App.vue -->
<template>
<Person ref="ren"/>
<button @click="test">测试</button>
</template>
<script lang="ts" setup name="App">
import Person from './components/Person.vue'
import {ref} from 'vue'
let ren = ref()
function test(){
console.log(ren.value.name)
console.log(ren.value.age)
}
</script>
<!-- 子组件Person.vue中要使用defineExpose暴露内容 -->
<script lang="ts" setup name="Person">
import {ref,defineExpose} from 'vue'
// 数据
let name = ref('张三')
let age = ref(18)
/****************************/
/****************************/
// 使用defineExpose将组件中的数据交给外部
defineExpose({name,age})
</script>

浙公网安备 33010602011771号