L
LebronJames
V1
2022/01/28阅读:124主题:绿意
Vue3 基础分享
Options API 和 Setup API 生命周期的比较
Options API | Hook inside setup |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
activated | onActivated |
deactivated | onDeactivated |
代码逻辑位置的比较

Options API
<script>
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
data () {
return {
repositories: [], // 1
filters: { }, // 3
searchQuery: '' // 2
}
},
computed: {
filteredRepositories () { }, // 3
repositoriesMatchingSearchQuery () { }, // 2
},
watch: {
user: 'getUserRepositories' // 1
},
methods: {
getUserRepositories () {
// using `this.user` to fetch user repositories
}, // 1
updateFilters () { }, // 3
},
mounted () {
this.getUserRepositories() // 1
}
}
</script>
Mixin 的缺点
-
很容易发生冲突:因为每个 mixin 的 property 都被合并到同一个组件中,所以为了避免 property 命名冲突,你需要了解其它mixin文件的逻辑。
-
可重用性是有限的:我们不能向 mixin 传递任何参数来改变它的逻辑,这降低了它们在抽象逻辑方面的灵活性。
使用 setup
1
<script>
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
setup (props) {
const { user } = toRefs(props)
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
watch(user, getUserRepositories)
return {
repositories,
getUserRepositories
}
},
}
</script>
2
<script>
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
setup (props) {
const { user } = toRefs(props)
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
watch(user, getUserRepositories)
const searchQuery = ref('')
const repositoriesMatchingSearchQuery = computed(() => {
return repositories.value.filter(
repository => repository.name.includes(searchQuery.value)
)
})
return {
repositories,
getUserRepositories,
searchQuery,
repositoriesMatchingSearchQuery
}
},
}
</script>
抽离需要复用的逻辑
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch } from 'vue'
export default function useUserRepositories(user) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
watch(user, getUserRepositories)
return {
repositories,
getUserRepositories,
}
}
Composition API
<script>
import { toRefs } from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true,
},
},
setup(props, context) {
// context.attrs Attribute (非响应式对象,等同于 $attrs)
// context.slots 插槽 (非响应式对象,等同于 $slots)
// context.emit 触发事件 (方法,等同于 $emit)
// context.expose 暴露公共 property (函数)
const { user } = toRefs(props)
const { repositories, getUserRepositories } = useUserRepositories(user) // 1
const { searchQuery, repositoriesMatchingSearchQuery } = useRepositoryNameSearch(repositories) // 2
const { filters, updateFilters, filteredRepositories } = useRepositoryFilters(
repositoriesMatchingSearchQuery,
) // 3
return {
repositories: filteredRepositories,
getUserRepositories,
searchQuery,
filters,
updateFilters,
}
},
}
</script>
Composition 语法糖
<script setup>
import { toRefs } from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'
const props = defineProps({
user: {
type: String,
required: true,
},
})
const { user } = toRefs(props)
const { repositories, getUserRepositories } = useUserRepositories(user) // 1
const { searchQuery, repositoriesMatchingSearchQuery } = useRepositoryNameSearch(repositories) // 2
const { filters, updateFilters, filteredRepositories } = useRepositoryFilters(
// 3
repositoriesMatchingSearchQuery,
)
</script>
ref 和 reactive
<script setup>
import { ref, reactive } from 'vue'
const eleRef = ref()
const num = ref(1)
const info = ref({ name: 'Lebron' })
console.log(num) // RefImpl {_shallow: false, dep: undefined, __v_isRef: true, _rawValue: 1, _value: 1, value: 1}
console.log(num.value) // 1
console.log(info) // RefImpl {_shallow: false, dep: undefined, __v_isRef: true, _rawValue: {user: 'Lebron'}, _value: Proxy, value: Proxy}
const originalObj = {
active: '',
loading: false,
list: Array(10).fill(50),
}
const responseObj = reactive(originalObj)
console.log(responseObj)
// Proxy {active: '', loading: false, list: Array(0)} [[Handler]]: Object [[Target]]: Object [[IsRevoked]]: false
</script>
1. ref和reactive的响应式有区别吗?
2. vue2和vue3的响应式有什么区别?
1. ref和reactive的响应式有区别吗?
-
当ref值的类型为对象时,使用是reactive进行代理 -
reactive只能定义对象
2. vue2和vue3的响应式有什么区别?
-
vue3使用ES6内置Proxy对象进行代理,并且vue3不会逐个遍历属性进行代理,遇到值是对象才会进行嵌套代理 -
Proxy可以实现拦截和自定义操作,比如属性查找、赋值、枚举、函数调用等
vue3一些其他方面的优化
-
编译阶段打上Flags -
diff算法优化 -
静态提升 -
事件监听缓存
Flags的类型
export const enum PatchFlags {
TEXT = 1,// 动态的文本节点
CLASS = 1 << 1, // 2 动态的 class
STYLE = 1 << 2, // 4 动态的 style
PROPS = 1 << 3, // 8 动态属性,不包括类名和样式
FULL_PROPS = 1 << 4, // 16 动态 key,当 key 变化时需要完整的 diff 算法做比较
HYDRATE_EVENTS = 1 << 5, // 32 表示带有事件监听器的节点
STABLE_FRAGMENT = 1 << 6, // 64 一个不会改变子节点顺序的 Fragment
KEYED_FRAGMENT = 1 << 7, // 128 带有 key 属性的 Fragment
UNKEYED_FRAGMENT = 1 << 8, // 256 子节点没有 key 的 Fragment
NEED_PATCH = 1 << 9, // 512
DYNAMIC_SLOTS = 1 << 10, // 动态 solt
HOISTED = -1, // 特殊标志是负整数表示永远不会用作 diff
BAIL = -2 // 一个特殊的标志,指代差异算法
}
未优化前
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_createElementVNode("div", { class: "content" }, " LebronJames "),
_createElementVNode("div", {
class: "item",
onClick: _ctx.onChange
}, " KevinDurant ", 8 /* PROPS */, ["onClick"])
]))
}
静态提升、事件监听缓存
const _hoisted_1 = /*#__PURE__*/_createElementVNode("div", { class: "content" }, " LebronJames ", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_hoisted_1,
_createElementVNode("div", {
class: "item",
onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.onChange && _ctx.onChange(...args)))
}, " KevinDurant ")
]))
}
diff算法优化
if (patchFlag > 0) {
if (patchFlag & PatchFlags.FULL_PROPS) {
patchProps(...)
} else {
if (patchFlag & PatchFlags.CLASS) {
if (oldProps.class !== newProps.class) {
hostPatchProp(...)
}
}
if (patchFlag & PatchFlags.STYLE) {
hostPatchProp(...)
}
if (patchFlag & PatchFlags.PROPS) {
for (...) {
...
if (...) {
hostPatchProp(...)
}
}
}
}
if (patchFlag & PatchFlags.TEXT) {
if (...) {
hostSetElementText(...)
}
}
}
vue2升级vue3需要注意的breaking change
-
filter 过滤器被移除 -
.sync 语法糖被移除 -
off、$off 被移除 -
可以使用多个 v-model -
在 setup 选项中 ref 需要声明对应的变量 -
v-if 比 v-for 拥有更高的优先级
作者介绍
L
LebronJames
V1