vue3 开发总结
本文是vue3官方文档的提炼和总结,主要是记录vue3开发常用到的知识点,方便后续开发的时候查询,避免一段时间没开发之后遗忘了,又需要重新看官网,比较费时。
官网vue2和vue3混杂在一起介绍比较,读起来比较费劲,本文是官网主要知识点的提炼和总结,定位于比较长时间没开发,已经遗忘的情况下,快速查询常用功能,不用再比较费劲地再看官网。初学者还是去官网学。
一、安装
-
通过脚手架 Vite:
npm init vite hello-vue3 -- --template vue # 或 yarn create vite hello-vue3 --template vue
-
通过脚手架 vue-cli:
npm install -g @vue/cli # 或 yarn global add @vue/cli vue create hello-vue3 # 选择 vue 3 preset
二、vscode 插件Volar
三、新增组合式api
作用:将逻辑相关的代码聚合在一个地方,使代码更清晰,更容易维护。
组合式api可以通过setup配置项目使用,不过这种写起来比较麻烦,不做介绍了,vue3.2 提供了单文件组件<script setup>作为setup配置项的语法糖衣,直接使用这种方式就好,官网看这里。以下是一些常用功能的写法。
顶层的绑定会被暴露给模板
<script setup>
import { capitalize } from './helpers' // 顶层的绑定会被暴露给模板
import MyComponent from './MyComponent.vue' // 组件引入后直接使用
// 变量,顶层的绑定会被暴露给模板
const msg = 'Hello!'
// 函数,顶层的绑定会被暴露给模板
function log() {
console.log(msg)
}
</script>
<template>
<div>{{ capitalize('hello') }}</div>
<div @click="log">{{ msg }}</div>
</template>
ref响应式变量
使用包含value的对象方式,是为了防止传递过程中响应性丢失。
<script setup>
import { ref } from 'vue'
// 响应式变量
const count = ref(0)
console.log(count.value)
</script>
<template>
<button @click="count++">{{ count }}</button>
<MyComponent />
</template>
补充:对于vue暴露出来的方法,如果不想每次都去写import,可以使用unplugin-auto-import插件,使用方法请自行百度。
定义props
<script setup>
// 定义props
const props = defineProps({
name: {
type: String,
default: 'xxxx',
}
})
console.log(props.name) // 读取
</script>
<template>
<div>{{name}}</div>
</template>
defineEmits
声明提供的事件,同事支持对事件进行校验。
vue3 v-on
的 .native
修饰符已被移除。没有用defineEmits什么的事件,都会被当做组件的原生组件,应用到根元素上。看这里
<script setup>
const emit = defineEmits(['change', 'delete'])
emit('change', param) // 触发
</script>
自定义事件还支持进行验证,详细看这里
defineExpose
暴露属性和方法给外部使用
<script setup>
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>
defineProps、defineEmits、defineExpose 不需要引入,可以直接使用。
生命周期
<script setup>
import { onMounted } from 'vue'
// 生命周期
onMounted(() => {
// do something....
})
</script>
此外
destroyed
生命周期选项被重命名为unmounted
beforeDestroy
生命周期选项被重命名为beforeUnmount
watch
<script setup>
import { watch } from 'vue'
watch(counter, (newValue, oldValue) => {
})
</script>
当侦听一个数组时,只有当数组被替换时才会触发回调。如果你需要在数组被改变时触发回调,必须指定 deep
选项。
computed
<script setup>
import { computed } from 'vue'
const twiceTheCounter = computed(() => counter.value * 2)
console.log(twiceTheCounter.value)
</script>
全局注册组件
import { createApp } from 'vue'
import MyComponent from '@/components/MyComponent'
const app = createApp({})
// 注册一个名为my-component的组件
app.component('my-component', MyComponent)
// 检索注册的组件(始终返回构造函数)
const MyComponent = app.component('my-component')
动态组件 setup 写法
<template>
<component :is="components.get(activeKey)" :type="iType" />
</template>
<script lang="ts" setup>
import { ref,defineAsyncComponent, markRaw } from "vue";
const activeKey = ref('A')
const components = markRaw(new Map<string, any>())
//组件1
components.set(
'A',
defineAsyncComponent(() => import('./A.vue'))
)
//组件2
components.set(
'B',
defineAsyncComponent(() => import('./B.vue'))
)
</script>
指令
全局注册指令使用app.directive,指令钩子已做了重命名,更好理解了。
过滤器
过滤器移除了,可以采用config.globalProperties定义全局函数替代。config.globalProperties本身是用于替代 Vue.prototype
,可以参考这里
在网上查到其他人注册全局方法的思路
setup 中代码变大怎么办
代码集中在setup中会使代码变大,可将代码按逻辑独立出去
import { ref, computed } from 'vue'
// 独立逻辑代码
export default function useSearch(param) {
const searchQuery = ref('')
const search = function(){
}
return {
searchQuery,
search
}
}
<script setup>
import search from 'search'
const { searchQuery, search} = useSearch(param)
</script>
与普通的 <script>
一起使用
<script setup>
可以和普通的 <script>
一起使用。普通的 <script>
在有这些需要的情况下或许会被使用到:
- 无法在
<script setup>
声明的选项,例如inheritAttrs
或通过插件启用的自定义的选项。 - 声明命名导出。
- 运行副作用或者创建只需要执行一次的对象。
<script>
// 普通 <script>, 在模块范围下执行(只执行一次)
runSideEffectOnce()
// 声明额外的选项
export default {
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// 在 setup() 作用域中执行 (对每个实例皆如此)
</script>
顶层 await
<script setup>
中可以使用顶层 await
。结果代码会被编译成 async setup()
:
<script setup>
const post = await fetch(`/api/post/1`).then(r => r.json())
</script>
另外,await 的表达式会自动编译成在 await
之后保留当前组件实例上下文的格式。
注意
async setup()
必须与 Suspense
组合使用,Suspense
目前还是处于实验阶段的特性。我们打算在将来的某个发布版本中开发完成并提供文档 - 如果你现在感兴趣,可以参照 tests 看它是如何工作的。
四、teleport vue 的传送门功能
<teleport to="#modals">
<div>A</div>
</teleport>
teleport 内部的元素将渲染到id为modals的元素中。
五、Provide / Inject
// 在入口中
app.provide('guide', 'Vue 3 Guide')
// 在子组件中
export default {
inject: {
book: {
from: 'guide'
}
},
template: `<div>{{ book }}</div>`
}
六、组件双向数据绑定 v-model实现变化
<my-component v-model:title="bookTitle"></my-component>
app.component('my-component', {
props: {
title: String
},
emits: ['update:title'],
template: `
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)">
`
})
还支持多个v-model绑定,和自定义修饰符看这里
七、css 支持通过v-bind动态设置
<script setup>
const theme = {
color: 'red'
}
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
color: v-bind('theme.color');
}
</style>
八、普通的ref获取元素
<template>
<input ref="input"/>
</template>
<script setup>
import { ref } from 'vue'
const input = ref(null)
// input.value即为该input元素
</script>
v-for 中的 Ref 数组
<script setup>
let itemRefs = []
const setItemRef = el => {
if (el) {
itemRefs.push(el)
}
}
</script>
<template>
<div v-for="item in list" :ref="setItemRef"></div>
</template>
九、响应式api
reactive
返回对象的响应式副本,看了网上一些人说:“ref正对基础数据类型,reactive针对对象”。其实试了一下,ref也可以用于复杂对象,建议模板中使用更多的是用ref,ref可以很轻松的对整个对象进行替换,如果是reactive,需要对每一个键进行赋值,才会变化。
toRaw
返回 reactive 或 readonly 代理的原始对象。这是一个“逃生舱”,可用于临时读取数据而无需承担代理访问/跟踪的开销,也可用于写入数据而避免触发更改。不建议保留对原始对象的持久引用。请谨慎使用。
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true
shallowReactive
创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)。
const state = shallowReactive({
foo: 1,
nested: {
bar: 2
}
})
// 改变 state 本身的性质是响应式的
state.foo++
// ...但是不转换嵌套对象
isReactive(state.nested) // false
state.nested.bar++ // 非响应式
与 reactive 不同,任何使用 ref 的 property 都不会被代理自动解包
ref
接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property
unref
如果参数是一个 ref,则返回内部值,否则返回参数本身
toRef
响应型对象一旦被销毁或展开,其响应式特性就会丢失。toRef
可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。
toRefs
将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。
const state = reactive({
foo: 1,
bar: 2
})
const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:
{
foo: Ref<number>,
bar: Ref<number>
}
*/
// ref 和原始 property 已经“链接”起来了
state.foo++
console.log(stateAsRefs.foo.value) // 2
stateAsRefs.foo.value++
console.log(state.foo) // 3
当从组合式函数返回响应式对象时,toRefs
非常有用,这样消费组件就可以在不丢失响应性的情况下对返回的对象进行解构/展开:
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2
})
// 操作 state 的逻辑
// 返回时转换为ref
return toRefs(state)
}
export default {
setup() {
// 可以在不失去响应性的情况下解构
const { foo, bar } = useFeatureX()
return {
foo,
bar
}
}
}
toRefs
只会为源对象中包含的 property 生成 ref。如果要为特定的 property 创建 ref,则应当使用 toRef
<script setup>
const { user } = toRefs(props)
console.log(user.value)
</script>
computed
接受一个具有 get
和 set
函数的对象,用来创建可写的 ref 对象。
watchEffect
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> logs 0
setTimeout(() => {
count.value++
// -> logs 1
}, 100)
watch
watch
需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生变化时被调用。
插槽没有什么变化,直接看文档
十、其他一些变化
1、支持了多根节点的组件
2、同一个元素上,v-if
比 v-for
优先级更高
3、createApp
返回一个应用实例,替代Vue 2.x 有许多全局 API 和配置,避免了测试期间的全局污染。
4、全局api采用具名导出,更好的支持 tree-shaking
5、<template v-for>
的 key
应该设置在 <template>
标签上 (而不是设置在它的子节点上)。
6、v-bind 的绑定顺序会影响渲染结果。
<!-- 模板 -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div id="blue"></div>
<!-- 模板 -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- 结果 -->
<div id="red"></div>
7、数式组件的性能提升可以忽略不计,因此我们建议只使用有状态的组件
8、异步组件用defineAsyncComponent定义,具体看这里,路由异步组件不用defineAsyncComponent,还是按vue-router路由懒加载来
9、$attrs
现在包含了所有传递给组件的 attribute,包括 class
和 style
$attrs
中的 attribute 将不再被自动添加到根元素中,而是由开发者决定在哪添加。
<template>
<label>
<input type="text" v-bind="$attrs" />
</label>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
10、data选项
-
非兼容:组件选项
data
的声明不再接收纯 JavaScriptobject
,而是接收一个function
。 -
非兼容:当合并来自 mixin 或 extend 的多个
data
返回值时,合并操作现在是浅层次的而非深层次的 (只合并根级属性)
11、过渡类名 v-enter
修改为 v-enter-from
、过渡类名 v-leave
修改为 v-leave-from
12、
没有特殊指令的标记 (v-if/else-if/else
、v-for
或 v-slot
) 的 <template>
现在被视为普通元素,并将渲染为原生的 <template>
元素,而不是渲染其内部内容。
13、监听组件生命周期
<template>
<child-component @vnode-updated="onUpdated">
</template>
14、被移除
1、$on
,$off
和 $once
实例方法已被移除,组件实例不再实现事件触发接口。
2、对内联模板特性的支持已被移除
3、$children
已被移除,采用$refs代替
4、$destroy
5、全局函数 set
和 delete
以及实例方法 $set
和 $delete
15、获取上下文对象
import { getCurrentInstance } from "vue";
setup(){
//解构赋值 设置别名that 也可不写 :that 直接ctx
let {ctx} = getCurrentInstance()
ctx.$forceUpdate()
}
十一、vue-router
vue-router 4采用vue3重写,大部分的 Vue Router API 都没有变化,变化部分可看这里,不过采用手脚架工具构建完之后,其实这些变化用到的很少。更常用到的是组合式api,与组合式api相关的可看这里。
十二、vuex 4
vuex 4 采用vue3重写,大部分都没什么变化,变化部分看这里, 不过采用手脚架工具构建完之后,其实这些变化用到的很少。更常用到的是组合式api,与组合式api相关的可看这里。
十三、截止至目前发现的一些问题
1、Vue-Meta 尚未支持vue3,只看到beta版的
2、element plus 在Safari 13 date-picker placeholder失效、也选不了时间。Safari15是正常的。
本文只提炼了开发中常用的知识点,详细可还是到官网学习,懂vue2的人可从迁移指南开始阅读。
其实,个人感觉,setup的方式反而使代码变得有些混乱了,一个变量你可能只是在某个函数和模板中用到,这时你可能需要放到函数附近。写着写着可能有其他函数也要用到,这时可能又得把它放到顶部。各种函数也散落一地,感觉又回到了jquery没有约束的时代。虽然可以将部分相关性比较大的代码独立出去,但其实挺麻烦。不知各位大佬怎么看。
更多推荐
所有评论(0)