关于vue.js:vue3新特性及对比-2x-的重要变更

42次阅读

共计 8127 个字符,预计需要花费 21 分钟才能阅读完成。

往年 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,所以也就有了更加优化的编译,同时实现了更加高效的组件初始化。

  1. Rewritten virtual dom implementation(重写了虚构 DOM)
  2. Compiler-informed fast paths(优化编译)
  3. More efficient component initialization(更高效的组件初始化)
  4. 1.3-2x better update performance(1.3~2 倍的更新性能)
  5. 2-3x faster SSR(2~3 倍的 SSR 速度)

Tree-shaking support(反对 Tree-shaking)

在大部分状况下,咱们并不需要 vue 中的所有性能,然而在之前的 vue 版本中,咱们没有一个适合的方法用来除去不须要的性能,而 Vue3 中,为了满足体积更小的需要,反对 Tree-shaking,也就意味着咱们能够按需要援用的内置的指令和办法。

  1. Most optional features (e.g. v-model, <transition>) are now tree-shakable(大多数可选性能(如 v-model、<transition>)当初都是反对 Tree-shaking 的。)
  2. Bare-bone HelloWorld size: 13.5kb. 11.75kb with only Composition API support.
  3. All runtime features included: 22.5kb. More features but still lighter than Vue 2.

Composition API

Composition API 次要是进步了代码逻辑的可复用性,并且将 Reactivity 模块独立进去,这也使得 vue 3 变得更加灵便地与其余框架组合应用。

  1. Usable alongside existing Options API(可与现有选项 API 一起应用)
  2. Flexible logic composition and reuse(灵便的逻辑组成和重用)
  3. Reactivity module can be used as a standalone library(Reactivity 模块能够作为独立的库应用)

Fragment, Teleport, Suspense

  1. 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”
  1. Teleport

Teleport 其实就是 React 中的 Portal。Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优良的计划。一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 款式时,但你须要子组件可能在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。

  • Previously known as <Portal>(原名为 <Portal>)
  • More details to be shared by @Linusborg
  1. 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 测试阶段。

  1. Codebase written in TS w/ auto-generated type definitions
  2. API is the same in JS and TS
  3. In fact, code will also be largely the same
  4. TSX support
  5. 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 这样的我的项目提供了很多便当。

  1. NativeScript Vue integration underway by @rigor789
  2. 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() 函数中应用.

  1. beforeCreate -> use setup()
  2. created -> use setup()
  3. beforeMount -> onBeforeMount
  4. mounted -> onMounted
  5. beforeUpdate -> onBeforeUpdate
  6. updated -> onUpdated
  7. beforeDestroy -> onBeforeUnmount
  8. destroyed -> onUnmounted
  9. errorCaptured -> onErrorCaptured

 import {onBeforeMount, onMounted} from 'vue'

 export default {setup() {onBeforeMount(() => {console.log('onBeforeMount')

         })

        onMounted(() => {console.log('onMounted')

        })

     },

 }

setup

setup() 函数是 vue3 中,专门为组件提供的新属性。它为咱们应用 vue3 的 Composition API 新个性提供了对立的入口。

  1. setup 函数相当于 vue2.x 中 beforeCreate 和 created, 在 beforeCreate 之后、created 之前执行
  2. setup 第一个形参,接管 props 数据
  3. 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

  1. provide() 和 inject() 能够实现嵌套组件之间的数据传递。
  2. 这两个函数只能在 setup() 函数中应用。
  3. 父级组件中应用 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 还有很多还须要多多学习 : )

正文完
 0