往年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还有很多还须要多多学习 : )