往年 7 月,尤大大发表 Vue 3 进入 RC 阶段,这意味着 Vue 3 内核 API 与实现已趋稳固。
Vue 作为一种渐进式框架,借鉴了 React 的组件化和虚构 DOM,借鉴了 Angular 的模块化和双向数据绑定。就框架的 API 而言,比照之下,它更加轻便简洁。对于 Vue 的益处这里不再赘述。
绝对 vue2.x,Vue3 做了很多重要的变更,特地是 Composition API 的引入。咱们晓得,Vue 现有 API 是通过「选项」组织代码的,随着性能的增长,简单组件的代码会变得越来越难保护,很多逻辑都无奈复用。开发的时候简单的组件千行代码 template/data/methods 来回翻,看到你头晕,而 vue3 能够让你在接手他人的代码时更容易理清逻辑关系。上面间接进入主题,咱们来看看 Vue3 都有哪些新个性,绝对于之前版本又有哪些变更。
一、3.0 比照 2.x 的重要变更
Vue 3.0 绝对与之前的版本,有 6 个方面的重要变更:
Performance(性能)
性能上,次要是优化了虚构 DOM,所以也就有了更加优化的编译,同时实现了更加高效的组件初始化。
- Rewritten virtual dom implementation(重写了虚构 DOM)
- Compiler-informed fast paths(优化编译)
- More efficient component initialization(更高效的组件初始化)
- 1.3-2x better update performance(1.3~2 倍的更新性能)
- 2-3x faster SSR(2~3 倍的 SSR 速度)
Tree-shaking support(反对 Tree-shaking)
在大部分状况下,咱们并不需要 vue 中的所有性能,然而在之前的 vue 版本中,咱们没有一个适合的方法用来除去不须要的性能,而 Vue3 中,为了满足体积更小的需要,反对 Tree-shaking,也就意味着咱们能够按需要援用的内置的指令和办法。
- Most optional features (e.g. v-model, <transition>) are now tree-shakable(大多数可选性能(如 v-model、<transition>)当初都是反对 Tree-shaking 的。)
- Bare-bone HelloWorld size: 13.5kb. 11.75kb with only Composition API support.
- All runtime features included: 22.5kb. More features but still lighter than Vue 2.
Composition API
Composition API 次要是进步了代码逻辑的可复用性,并且将 Reactivity 模块独立进去,这也使得 vue 3 变得更加灵便地与其余框架组合应用。
- Usable alongside existing Options API(可与现有选项 API 一起应用)
- Flexible logic composition and reuse(灵便的逻辑组成和重用)
- Reactivity module can be used as a standalone library(Reactivity 模块能够作为独立的库应用)
Fragment, Teleport, Suspense
- Fragment
在书写 vue2 时,因为组件必须只有一个根节点,很多时候会增加一些没有意义的节点用于包裹。Fragment 组件就是用于解决这个问题的(这和 React 中的 Fragment 组件是一样的)。
- No longer limited to a single root node in templates(<template> 中不再局限于繁多的根节点)
-Manual render functions can simply return Arrays(render 函数能够返回数组)
- “Just works”
- Teleport
Teleport 其实就是 React 中的 Portal。Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优良的计划。一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 款式时,但你须要子组件可能在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。
- Previously known as <Portal>(原名为 <Portal>)
- More details to be shared by @Linusborg
- Suspense
同样的,这和 React 中的 Supense 是一样的。Suspense 让你的组件在渲染之前进行“期待”,并在期待时显示 fallback 的内容。
- Wait on nested async dependencies in a nested tree
- Works with async setup()(与 async 函数 setup() 配合应用)
- Works with Async Components(与 Async 组件配合应用)
Better TypeScript support(更好的 TypeScript 反对度)
vue3.0 对 TS 的反对度更高了,同时也反对 TSX 的应用;API 在 JS 与 TS 中的应用雷同;类组件依然可用,然而须要咱们引入 vue-class-component@next,该模块目前还处于 alpha 测试阶段。
- Codebase written in TS w/ auto-generated type definitions
- API is the same in JS and TS
- In fact, code will also be largely the same
- TSX support
- Class component is still supported (vue-class-component@next is currently in alpha)
Custom Renderer API(自定义的 Renderer API)
自定义 render 会提供一个 API 用来创立自定义的 render,因而不再须要为了自定义一些性能而 fork Vue 的代码。这个个性给 Weex 和 NativeScript Vue 这样的我的项目提供了很多便当。
- NativeScript Vue integration underway by @rigor789
- Users already experimenting w/ WebGL custom renderer that can be used alongside a normal Vue application (Vugel)
二、Composition API 等外围个性
在 vue3 中引入了 Composition API(组合 API),应用纯函数分隔复用代码,让逻辑变得清晰。
对于 VCA,有人说跟 react 的 hooks 很像,咱们来看看作者尤大的介绍:
Lifecycle Hooks
新版的生命周期函数,能够按需导入到组件中,且只能在 setup() 函数中应用.
beforeCreate-> use setup()created-> use setup()- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
import {onBeforeMount, onMounted} from 'vue'
export default {setup() {onBeforeMount(() => {console.log('onBeforeMount')
})
onMounted(() => {console.log('onMounted')
})
},
}
setup
setup() 函数是 vue3 中,专门为组件提供的新属性。它为咱们应用 vue3 的 Composition API 新个性提供了对立的入口。
- setup 函数相当于 vue2.x 中 beforeCreate 和 created, 在 beforeCreate 之后、created 之前执行
- setup 第一个形参,接管 props 数据
- setup 第二个形参是一个上下文对象, 在 setup() 函数中无法访问到 this, 能够用这个 context 来拜访
export default {
props: {str: String},
setup(props, context) {console.log(props) // str
console.log(context) // attrs, slots, parent, root, emit, refs
},
}
reactive
reactive() 函数接管一个一般对象,返回一个响应式的数据对象。
import {reactive} from 'vue'
export default {setup() {const state = reactive({count: 0}) // 创立响应式数据对象
return state // 将响应式数据对象 return 进来,供 template 应用
}
}
ref
ref() 函数依据给定的值创立一个响应式的数据对象,返回值是一个对象,这个对象上只蕴含一个 .value 属性
import {ref} from 'vue'
export default {setup() {const count = ref(0) // 创立响应式数据对象 count,初始值为 0
console.log(count.value) // 在 setup 内拜访 count 值须要.value 属性才能够,但在 template 中能够间接拜访
return {count}
},
}
toRefs
toRefs() 函数能够将 reactive() 创立进去的响应式对象,转换为一般的对象,只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据
import {reactive, toRefs} from 'vue'
export default {setup() {const state = reactive({count: 0, name:'weedsFly'}) // 用 reactive 集中创立多个响应式对象
const add = () => { // methods 写在 setup 内
state.count++
}
return {
// ...state, // 应用开展运算符后 用 reactive 创立的响应式数据 变成了 固定的值
...toRefs(state), // 能够用 toRefs 函数 将传进来的非响应式对象 转成 ref() 类型的响应式数据
add
}
},
}
ref 援用
通过 ref() 还能够援用页面上的元素或组件,这点和 vue2 的 ref 概念相似。
1. 元素援用
import {ref, onMounted} from 'vue'
export default {setup() {const h1Ref = ref(null) // 创立一个 DOM 援用
onMounted(() => { // 在 DOM 首次加载结束之后,能力获取到元素的援用
h1Ref.value.style.color = 'pink' // h1Ref.value 是原生 DOM 对象
})
return {h1Ref}
}
}
2. 组件援用
// 父组件:import {ref} from 'vue'
export default {setup() {const compRef = ref(null) // 创立一个组件的 ref 援用
showCompData = () => { // 展现子组件中 count 的值
console.log(compRef.value.count)
}
return {
compRef,
showCompData
}
}
}
// 子组件:import {ref} from 'vue'
export default {setup() {const count = ref(0) // 定义响应式的数据
return {count}
}
}
computed
omputed() 用来创立计算属性,computed() 函数的返回值是一个 ref 的实例。
1.computed 创立只读的计算属性 (传入一个 function 函数,能够失去一个只读的计算属性)
import {ref, computed} from 'vue'
export default {setup() {const count = ref(0)
const computedCount = computed(() => count.value + 1)
computedCount.value = 9 // computed value is readonly.
return {
count,
computedCount
}
},
}
2.computed 创立可读可写的计算属性
import {ref, computed} from 'vue'
export default {setup() {const count = ref(0)
const computedCount = computed({get: () => count.value + 1,
set: val => {count.value = val - 1}
})
computedCount.value = 100 // 为计算属性赋值的操作,会触发 set 函数
console.log(count.value) // 触发 set 函数后,count 的值会被更新
return {
count,
computedCount,
computedCount
}
},
}
watch
1. 监督单个数据源变动
- 监督单个 reactive 创立的数据源
import {reactive, watch} from 'vue'
export default {setup() {const state = reactive({count: 100})
watch(() => state.count,
(newVal, oldVal) => {console.log(newVal, oldVal)},
{lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码
)
setTimeout(() => {state.count++}, 1500)
}
}
- 监督单个 ref 创立的数据源
import {ref, watch} from 'vue'
export default {setup() {const count = ref(100)
watch(
count,
(newVal, oldVal) => {console.log(newVal, oldVal)},
{lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码
)
setTimeout(() => {count++}, 1500)
}
}
2. 监督多个数据源
- 监督多个 reactive 创立的数据源
import {reactive, watch} from 'vue'
export default {setup() {const state = reactive({count: 100, name: 'Laiyj'})
watch([() => state.count, () => state.name],
([newCount, newName], [oldCount, oldName]) => {console.log(newCount, oldCount)
console.log(newName, oldName)
},
{lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码
)
setTimeout(() => {
state.count++
state.name = 'Lucy'
}, 1000)
}
}
- 监督多个 ref 创立的数据源
import {ref, watch} from 'vue'
export default {setup() {const count = ref(100)
const name = ref('Laiyj')
watch([count, name],
([newCount, newName], [oldCount, oldName]) => {console.log(newCount, oldCount)
console.log(newName, oldName)
},
{lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码
)
setTimeout(() => {
count++
name = 'Lucy'
}, 1000)
}
}
3. 革除 watch 监督
import {ref, watch} from 'vue'
export default {setup() {const count = ref(100)
const stop = watch( // 创立监督,并失去 进行函数
count,
(newVal, oldVal) => {console.log('I am watching.')
console.log(newVal, oldVal)
},
{lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码
)
setTimeout(() => {count++}, 1000)
const clearWatch = () => {stop()
}
return {
count,
clearWatch
}
}
}
4. 在 watch 中革除有效的异步工作(与节流防抖同效)
import {ref, watch} from 'vue'
export default {setup() {const keyword = ref('')
const asyncPrint = (val) => { // 执行异步工作,并失去敞开异步工作的 timerId
return setTimeout(() => {console.log(val)
}, 1000)
}
watch(
keyword,
(newVal, oldVal, onClean) => {const timeId = asyncPrint()
onClean(() => {clearTimeout(timeId)}) // 如果 watch 监听被反复执行了,则会先革除上次未实现的异步工作
}
)
return {keyword}
}
}
provide & inject
- provide() 和 inject() 能够实现嵌套组件之间的数据传递。
- 这两个函数只能在 setup() 函数中应用。
- 父级组件中应用 provide() 函数向下传递数据;子级组件中应用 inject() 获取下层传递过去的数据。
// 父组件:import {provide, watch} from 'vue'
export default {setup() {const color = ref('red')
provide('themeColor', color) // 父组件通过 provide 函数向子级组件共享数据(不限层级)provide('要共享的数据名称', 被共享的数据)
const changeColor = (val) => {color.value = val}
return {
color,
changeColor
}
}
}
// 子级组件:import {inject, watch} from 'vue'
export default {setup() {const color = inject('color') // 调用 inject 函数时,通过指定的数据名称,获取到父级共享的数据
return {color}
}
}
以上只列举了几个比拟根底重要的 API,vue3 还有很多还须要多多学习 : )