Vue3.0 整体优势
- proxy数据劫持代替object.defineProperty(特点:可以监听数组变化,可以直接修改对象属性等);
- Composition API,更好的逻辑复用和代码组织;
- 优化VirtualDom,使其编译更快,体积更小;
- 更好的TypeScript支持,不再需要借助装饰器;
Vue3.0 注意事项
- vue3.0页面中用到的API都需要手动引入
- 所有的逻辑代码都要写在setup方法中
- template中用到的变量和方法必须return出去才可以访问
- 书写TS将script标签属性lang="ts"即可
Vue3.0 生命周期变化
注意:所有生命周期API需要从vue按需引入,并且要在setup函数中调用:onMounted(()=>{...codeing })
- beforeCreate
>>
setup
- created
>>
setup
- beforeMount
>>
onBeforeMount
- mounted
>>
onMounted
- beforeUpdate
>>
onBeforeUpdate
- updated
>>
onUpdated
- beforeDestory
>>
onBeforeUnmount
- destoryed
>>
onUnmounted
Vue3.0 API的使用
1. 组件的声明
- 普通组件:defineComponent
- 异步组件:defineAsyncComponent
<script lang="ts" >
import {defineComponent} from 'vue'
const Home = defineComponent({
name: 'Home',
})
export default Home
</script>
2. 组件的引入 和 props类型检测
<script lang="ts" >
import Helloword from '../compontents/helloword'
const Home = defineComponent({
name: 'Home',
// 使用子组件
components: {
Helloword
},
// 检测父组件传入的Props
props: {
user: {
type: String,
required: true
}
}
})
export default Home
</script>
3. defineProps 和 defineEmits
- 在
<script setup>
中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits
- defineProps 和 defineEmits 都是只在
<script setup>
中才能使用的编译器宏。他们不需要导入且会随着 <script setup>
处理过程一同被编译掉
<script lang="ts" setup>
// ......V3+JS
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// ......V3+TS
const props = defineProps<{
foo: string
bar?: number
}>()
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
// ......V3+TS接口
interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
})
</script>
4. setup
- 提示:在 setup() 函数中无法访问this
- 执行时机:beforeCreate 之后 created之前
<script lang="ts" >
const Home = defineComponent({
name: 'Home',
setup(props, context) {
}
})
export default Home
</script>
5. ref
- 作用:ref() 函数用来根据给定的值创建一个响应式的数据对象
- 返回:ref() 函数调用的返回值是一个对象,这个对象上只包含一个 .value 属性
- 使用:.value 是在 setup 函数中访问 ref 包装后的对象时才需要加的,在 template 模板中访问时是不需要的,因为在编译时,会自动识别其是否为 ref 包装过的
- 注意:声明新的 ref 会覆盖旧的 ref
<template>
<div>{{year}}</div>
</template>
<script lang="ts" >
import {ref} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
let year = ref<string>('2021')
year.value = '2022'
return {
year
}
}
})
export default Home
</script>
6. reactive
- 作用:用来创建一个响应式的数据对象
- 注意:创建的变量具有 深层响应 的效果,该API也很好地解决了Vue2通过 defineProperty 实现数据响应式的缺陷
- 使用建议:(有的同学可能会比较疑惑,ref 和 reactive 的使用有点像)
- 基本类型值(String 、Nmuber 、Boolean 等)或单值对象(类似像 {count: 3} 这样只有一个属性值的对象)使用 ref
- 引用类型值(Object 、Array)使用 reactive
<script lang="ts" >
interface Student {
name: string,
age: string | number,
class: string
}
interface Todo {
text: string,
done: boolean
}
import {reactive} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const state = reactive({ count: 0})
const year = ref<string>('2021')
// 当把 ref() 创建出来的响应式数据对象,挂载到 reactive() 上时,会自动把响应式数据对象展开为原始的值,不需通过 .value 就可以直接被访问
const state2 = reactive(year)
// TS给定变量类型的几种方式
const studentA = reactive<Student>({ name: 'studentA', age: 11, class: 'studentA'})
const studentB: Student = reactive({ name: 'studentB', age: 22, class: 'studentB'})
const studentC = reactive({ name: 'studentC', age: 33, class: 'studentC'}) as Student
const todoList = reactive<Array<Todo>>([
{ text: '待办01', done: true},
{ text: '待办02', done: false}
])
return {
state2,
todoList
}
}
})
export default Home
</script>
7. shallowReactive
- 作用:用来创建一个响应式的数据对象(浅层响应,外层被proxy包裹实现响应,内层不更新视图)
- 场景:用作性能优化API,考虑到如果一个对象层级比较深,那么每一层都用 Proxy 包装后,对于性能是非常不友好的
<script lang="ts" >
import {shallowReactive} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const deepobj = {
a: 1,
first: {
b: 2,
second: {
c: 3
}
}
}
const newdeepobj = shallowReactive(deepobj)
newdeepobj.a = 11 // 第一层改变,更新视图
newdeepobj.first.b = 22 // 其它层级,不会更新视图
return {
newdeepobj
}
}
})
export default Home
</script>
8. isRef
- 作用:isRef() 用来判断某个值是否为 ref() 创建出来的对象
<script lang="ts" >
import {ref, isRef} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const year = ref('2021')
const getVal = isRef(year) ? year.value : '默认年份'
return {
getVal
}
}
})
export default Home
</script>
9. toRef
- 作用:toRef 是将某个对象中的某个值转化为响应式数据,
- 参数:其接收两个参数,第一个参数为 obj 对象;第二个参数为对象中的属性名
- 注意:
- ref 是对传入数据的拷贝;toRef 是对传入数据的引用
- ref 的值改变会更新视图;toRef 的值改变不会更新视图
<script lang="ts" >
import {toRef} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const data = {count: 3}
const newdata = toRef(data, 'count')
return {
getVal
}
}
})
export default Home
</script>
10. toRefs
- 作用:将传入的对象里所有的属性的值都转化为响应式数据对象,该函数支持一个参数
- toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象
- 只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据
<script lang="ts" >
import {reactive, toRefs} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const state = reactive({ name: 'zs', age: 22, gender: 0 })
const newstate = toRefs(state) // 转为普通数据
const obj = { name: 'ls', age: 20, gender: 1 }
const newobj = toRefs(obj) // 转为响应式数据
return {}
}
})
export default Home
</script>
11. shallowRef
- 作用:创建数据对象(浅层监听,只监听value实现响应)
- 场景:一样是拿来做性能优化的
<script lang="ts" >
import {shallowRef, triggerRef} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const multistoreyobj ={
a: 1,
first: {
b: 2,
second: {
c: 3
}
}
}
const newmultistoreyobj= shallowRef(multistoreyobj)
newmultistoreyobj.value.first.b = 8// 场景一: 改变数据,但不会更新视图
newmultistoreyobj.value.first.second.c = 9// 改变数据,但不会更新视图
newmultistoreyobj.value = {// 场景二: 将整个.value 重新赋值了,视图就立马更新了
a: 11,
first: {
b: 22,
second: {
c: 33
}
}
}
return {}
}
})
export default Home
</script>
12. triggerRef
// 接着上个示例代码看
newmultistoreyobj.value.first.b = 8// 改变数据不触发更新
triggerRef(newmultistoreyobj)// 场景三: 调用triggerRef方法驱动更新
13. toRaw
- 作用:用于获取 ref 或 reactive 对象的原始数据的
<script lang="ts" >
import {reactive, toRaw} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const olddatas = {
name: 'olddatas',
age: 11
}
const newdatas = reactive(olddatas) // 包装旧数据
const raw = toRaw(newdatas)// 拿到数据源 (注意:如果操作的是ref对象,参数需要写成newdatas.value)
raw.age = 111// 由于是引用数据源,所以修改数据源后引用的一方也会改变,但不触发更新 (所以:可用作性能优化)
return {}
}
})
export default Home
</script>
14. markRaw
- 作用:可以将原始数据标记为不可被追踪,即使用ref或reactive将其包装,仍无法实现数据响应式
- 参数:其接收一个参数,即原始数据,并返回被标记后的数据
<script lang="ts" >
import {reactive, markRaw} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const objA = { a: 1}
const rawA = markRaw(objA)// 通过markRaw标记原始数据objA, 使其数据更新不再被追踪
const stateA = reactive(raw)// 试图用reactive包装raw, 使其变成响应式数据
stateA.age = 90//响应无效:修改数据,不触发更新
return {}
}
})
export default Home
</script>
15. computed
<script lang="ts" >
import {ref, computed} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const refCount = ref(1)
let computedCount = computed(() => refCount.value + 1)
console.log(computedCount) // 读取
return {}
}
})
export default Home
</script>
<script lang="ts" >
import {ref, computed} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const refCount = ref(1)
let computedCount2 = computed({
get: () => refCount.value + 1,
set: val => refCount.value = val - 5
})
console.log(computedCount2.value) // 读取
computedCount2.value = 10 // 写入
console.log(computedCount2.value)
console.log(refCount.value)
return {}
}
})
export default Home
</script>
16. watch
- 注意:组件第一次创建的时候会触发一次,默认是渐层的监听我们指定的数据,需要深层配置deep:true
- 参数:watch( source, cb, [options] )
- source:可以是表达式或函数,用于指定监听的依赖对象
- options:可配置参数,可以配置的属性有 immediate(初始化立即触发回调函数)、deep(深度监听)
- 几种使用场景:
- 指定要监视的数据源
<script lang="ts" >
import {ref, watch} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
let refCount = ref<string>('20')
let year = ref<string>('2021')
let month = ref<string>('11')
watch(refCount, () => {console.log('监听refCount',refCount.value)})
watch(
[year, month],
([newYear, newMonth], [oldYear, oldMonth]) => {
console.log('监听year',newYear,oldYear)
console.log('监听month',newMonth,oldMonth)
}
)
return {}
}
})
export default Home
</script>
- 清除监视
在 setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。
如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可
<script lang="ts" >
import {watch} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
let refCount = ref<string>('20')
const stop = watch(refCount, () => {})
stop()
return {}
}
})
export default Home
</script>
- 清除无效的异步任务
有时候,当被 watch 监视的值发生变化时,或 watch 本身被 stop 之后,我们期望能够清除那些无效的异步任务,此时,watch 回调函数中提供了一个函数来执行清除的工作。这个清除函数会在如下情况下被调用:
<script lang="ts" >
import {watch} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
let refCount = ref<string>('20')
const asyncPrint = (val: any) => {// 异步任务
return setTimeout(() => {
console.log(val)
},1000)
}
watch(
refCount,
(newValue, oldValue, onCleanup) => {
const timerId = asyncPrint(refCount)// 执行异步任务,并得到关联的timerId
onCleanup(() => clearTimeout(timerId))// 如果watch监听被重复执行,则会清除上次未完成的任务
}
)
return {}
}
})
export default Home
</script>
17. watchEffect
- 作用:监听API
- 它与 watch 的区别主要有以下几点:
<script lang="ts" >
import {watchEffect} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const stateB = reactive({ count: 0, name: 'zs' })
setTimeout(() => {
stateB.count ++
stateB.name = 'ls'
}, 1000)
watchEffect(() => {
console.log(stateB.count) // 初始化时打印:0,zs
console.log(stateB.name) // 1秒后打印:1,1s
})
return {}
}
})
export default Home
</script>
18. getCurrentInstance
- 作用:Vue3中无法访问this,可以用到getCurrentInstance方法访问实例对象
<script lang="ts" >
import {getCurrentInstance} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
// instance.ctx接近this的概念 instance.proxy是包装过的响应式this
const instance = getCurrentInstance()
console.log(instance)
return {}
}
})
export default Home
</script>
19. useStore
- 作用:Vue3中拿不到this,instance.ctx里也没有store,只能用vuex引出的useStore方法使用仓库
<script lang="ts" >
import {store} from 'vuex'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const store = useStore()// 获取 vuex 实例
console.log(store)
return {}
}
})
export default Home
</script>
20. useRoute & useRouter
<script lang="ts" >
import {useRoute, useRouter} from 'vuex'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const route = useRoute() // 相当于 vue2 中的 this.$route
const router = useRouter() // 相当于 vue2 中的 this.$router
console.log(route,router)
return {}
}
})
export default Home
</script>
21. provide & inject
- 作用:provide() 和 inject() 可以实现嵌套组件之间的数据传递。
- 提示:这两个函数只能在 setup() 函数中使用。
- 使用:父级组件中使用 provide() 函数向下传递数据;子级组件中使用 inject() 获取上层传递过来的数据。
import {provide} from 'vue'
// 父组件向下传递
const color = ref('orange')
provide('themecolor',color)
// 子组件接收变量
const resultcolor = inject('themecolor')
22. 节点的引用 -- dom的引用
<template>
<h3 ref="h3Ref">h3的DOM引用</h3>
<button @click="clickBtn">弹窗</button>
</template>
<script lang="ts" >
import {ref, Ref} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const method = {
clickBtn: () => {
let color = h3Ref.value.style.color
alert(color)
}
}
const h3Ref:Ref = ref(null) // 创建一个响应式dom的引用,传入null
onMounted(() => {// DOM首次加载完毕,才能取到元素
h3Ref.value.style.color = 'red' // 为DOM元素设置样式颜色 , h3Ref.value是原生DOM对象
// (h3Ref as Ref).value.style.color = 'red'// 初始变量不给定Ref类型的话 可以使用断言语法规避报错
})
return {
...method, // 一次性展开所有方法
h3Ref, // 把DOM的引用抛出使用
}
}
})
export default Home
</script>
23. 节点的引用 -- 组件的引用
<template>
<!-- 子组件 -->
<HelloWorld ref="comRef" msg="Welcome to Your Vue.js + TypeScript App"/>
</template>
<script lang="ts" >
import {ref, Ref} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
const comRef: any = ref(null)
console.log(comRef.value.str1) // 打印子组件的变量str1值
comRef.valye.setStr1() // 调用子组件的方法设置str1值
console.log(comRef.value.str1) // 打印发现子组件的str1被改变了
return {
comRef // 把组件的引用抛出使用
}
}
})
export default Home
</script>
24. nextTick
- 在 Vue 3 中,全局和内部 API 都经过了重构,并考虑到了 tree-shaking 的支持。因此,对于 ES 模块构建版本来说,全局 API 现在通过具名导出进行访问
- 通过这一更改,如果模块打包工具支持 tree-shaking,则 Vue 应用中未使用的全局 API 将从最终的打包产物中排除,从而获得最佳的文件大小
Vue3.0 CSS的使用
1. 状态驱动的动态 CSS
<template>
<button class="dynamic">状态驱动的动态 CSS</button>
</template>
<script lang="ts" >
import {ref} from 'vue'
const Home = defineComponent({
name: 'Home',
setup(props, context) {
let btnBg = ref<string>('blue')
setTimeout(()=>{btnBg.value='orange'}, 2000)
return {
btnBg,
}
}
})
export default Home
</script>
<style lang="less" scoped> // scoped隔离样式作用域,不需要任何的 polyfill,通过PostCss转换内容实现
.dynamic{
/* 绑定变量 */
background: v-bind(btnBg)
}
</style>
2. 深度选择器
- 解释:处于 scoped 样式中的选择器如果想要做更“深度”的选择
- 注意:通过 v-html 创建的 DOM 内容不会被作用域样式影响,但你仍然可以使用深度选择器来设置其样式。
<template>
<div class="home">
</div>
</template>
<script lang="ts" >
const Home = defineComponent({
name: 'Home',
setup(props, context) {
return {}
}
})
export default Home
</script>
<style lang="less" scoped>
.home :deep(.btn_a) {
background: red
}
</style>
3. 插槽选择器
- 解释:默认情况下,作用域样式不会影响到
<slot/>
渲染出来的内容,因为它们被认为是父组件所持有并传递进来的。
- 作用:使用 :slotted 伪类以确切地将插槽内容作为选择器的目标
<style lang="less" scoped>
:slotted(div) {
background: #000
}
</style>
4. 全局选择器
- 解释:如果想让其中一个样式规则应用到全局,比起另外创建一个
<style>
,可以使用 :global 伪类来实现
<style lang="less" scoped>
:global(.background_red) {
background: red
}
</style>
5. 混合使用局部与全局样式
- 解释:你也可以在同一个组件中同时包含作用域样式和非作用域样式:
<style 标签和内容
<style scope 标签和内容
6. <style module>
-
<style module>
标签会被编译为 CSS Modules 并且将生成的 CSS 类作为 $style 对象的键暴露给组件
- 对生成的类做 hash 计算以避免冲突,实现了和 scope CSS 一样将 CSS 仅作用于当前组件的效果。
<style module>
.btn_b {
background: brown
}
</style>
7. 自定义注入名称
- 你可以通过给 module attribute 一个值来自定义注入的类对象的 property 键
<style module="moduleC">
.btn_c {
background: skyblue
}
</style>
8. 与组合式 API 一同使用
<style module="moduleD">
/* 默认, 返回 <style module> 中的类
useCssModule()
命名, 返回 <style module="classes"> 中的类
useCssModule('classes') */
.btn_d {
background: greenyellow
}
</style>