vue3.0有哪些新特性

vue3.0新特性有:1、性能比vue2.x块1.2~2倍;2、支持tree-shaking;3、引入了Composition API;4、暴露了自定义渲染API;5、新增三个组件(Fragment、Teleport、Suspense)等。

本教程操作环境:windows7系统、vue3.0版,DELL G3电脑。

vue3 带来的六大新特性

  • Performance:性能比vue2.x块1.2~2倍
  • Tree shaking support:支持按需编译,体积更小
  • Composition API:组合API,类似React Hooks
  • Custom Renderer API:暴露了自定义渲染API
  • Fragment,Teleport(Protal),Suspense:新增三个组件
  • Better TypeScript support:更好的支持TS

Performance

Vue3.0在性能方面比Vue2.x快了1.2~2倍。

  • 重写虚拟DOM的实现

  • 运行时编译

  • 静态提升与事件侦听器缓存

  • SSR 速度提高

Three-shaking support

Vue3.x中的核心API都支持tree-shaking,这些API都是通过包引入的方式而不是直接在实例化时就注入,只会对使用到的功能或特性进行打包(按需打包),这意味着更多的功能和更小的体积。

Composition API

Vue2.x中,我们通常采用mixin来复用逻辑代码,使用起来虽然方便,但也存在一些问题:代码来源不清晰、方法属性可能出现冲突。因此,Vue3.x引入了Composition API(组合API),使用纯函数分割复用代码。和React Hooks的概念相似。

  • 更好的逻辑复用和代码组织
  • 更好的类型推导

Fragment、Teleport、Suspense

新增三个组件。

Fragment

在书写Vue2.x时,由于组件必须是一个根结点,很多时候会添加一些没有意义的节点用于包裹。Fragment组件就是用于解决这个问题的(这和React 中的Fragment组件是一样的)。

Teleport

Teleport其实就是React中的Portal。Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。

Suspense

同样的,这和React中的Supense是一样的。

Suspense 让你的组件在渲染之前进行“等待”,并在等待时显示 fallback 的内容。

Better TypeScript support

Vue3.x采用TypeScript重写,开发者使用Vue3.x时可以充分体验TS给编码带来的便利。

Custom Renderer API

这个API定义了虚拟DOM的渲染规则,这意味着使用自定义API可以达到跨平台的目的。

最后:Vue3.0 是如何变快的?

与 react-hook 对比

compositionAPI 实践 Demo(并与 hook 的对比)常见吸顶及上拉加载 demo。

三、 其他改动

//hook
 useEffect(() => {
  alert('组件挂载')
   return ()=>{
     //组件卸载,清除副作用
     alert('组件卸载')
   }
 },[]);

 //compositonApi
 onMounted(()=>{
   alert('组件挂载')
 })
 onUnmounted(()=>{
   alert('组件卸载')
 })

vue-compositonApi 还是熟悉的 vue2.x 生命周期,没有增加新的理解成本

  • diff 算法优化

    • Vue2 中的虚拟dom 是进行全量对比
    • Vue3 新增静态标记
  • hoistStatic 静态提升

    • Vue2 中无论元素是否参与更新,每次都会重新创建,然后在渲染
    • Vue3 中对于不参与更新的元素,会做静态提升,只被创建一次,在渲染时直接复用即可
  • cacheHandlers 事件侦听器缓存

    • 默认情况下默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化,但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可
  • ssr 渲染

    • 当有大量静态的内容的时候,这些内容会被当作纯字符串推进一个buffer里面,即使存在动态的绑定,会通过模版插值嵌入进去,这样会比通过虚拟dom来渲染的快上很多很多
    • 当静态内容大到一定量级的时候,会用_createStaticVNode方法在客户端去生成一个static node。这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。

    一文读懂vue3新特性

    vue3.0 的新变化

    一、Proxy(核心原理)

  • 动机

  • 由于 ES5 Object.defineProperty 的限制,Vue 不能检测数组和对象的一些特殊变化。
  • // vue 2.x
    // 对于Object类型
    
    const vm = new Vue({
      data:{
          a:1
      }
    })
    
    // vm.a 是响应式的
    
    vm.b = 2
    // vm.b 新增属性是非响应式的
    
    // 对于Array类型
    
    const vm = new Vue({
      data: {
        items: ['a', 'b', 'c']
      }
    })
    
    vm.items[1] = 'x' // 不是响应性的 (通过索引改变一个普通值)
    vm.items.length = 2 // 不是响应性的 (修改length)
    
    

    vue3.x 不存在这些限制。proxy 是针对整个对象层面的代理拦截,而非 defineProperty 针对属性层面做的劫持。

    Proxy 实现响应式 Demo

    function reactive(target){
      if(!isObject(target)){
        return target
      }
    
      const handlers = {
        //属性读取触发get()方法
        get(target,key,receiver){
          const res = Reflect.get(target,key,receiver)
          return res
        },
        //属性设置触发set()方法
        set(target,key,value,receiver){
          trigger(target,key)
          const res = Reflect.set(target,key,value,receiver)
          return res
        },
        //数据删除触发deleteProperty()方法
        deleteProperty(target,key){
          const res = Reflect.deleteProperty(target,key)
          return res
        },
      }
      const observerd = new Proxy(target,handlers)
      return observerd
    }
    //对象
    let obj = {
      name:'zyd',
      age:26
    }
    let obj_= reactive(obj)
    // obj_.name = 'zyd1'
    // obj_.style = '1'
    //数组
    let arr = new Array(5).fill().map((item,i)=>i)
    let arr_ =  reactive(arr)
    // arr_.push(5)
    arr_[1] = 100
    arr_[100] = 100
    // arr_.length = 0
    
  • Proxy 比 defineProperty 拥有更好的新标准的性能红利。
  • 缺陷

    不支持 ie11 兼容性测试
  • 二、 Composition-API (核心 Api)

    字面理解:组合 Api(vue 希望通过功能函数的组合去实现逻辑拆分与复用)

  • 动机

  • 横切点关注问题

    Options 与 Class Api,代码组织不够聚合,无法按功能去进行代码组织,导致代码散落在 data、生命周期、watch、computed 里。

  • 逻辑拆分与复用

    vue2.x 代码复用的主要方式是提取可复用组件;纯的计算方法,可以提取为公共方法;但有些不需要模版的公共逻辑(并且与状态、事件、生命周期等紧密关联),就很难抽取,之前的 mixin、高阶组件等方案也都有他们各自带来的弊端。
    vue3.x 全新的 composition-API,可以完美解决这些问题,思路与 react-hook 类似,用负责单一功能函数进行组合。

  • 用法

    setup

    • setup 是一个新的组件选项,(它充当在组件内部使用 Composition API 的入口点。)

      // book.vue
        export default {
            props: {
              title: String
            },
            setup(props,context:{attrs, slots, emit}) {
              console.log(props.title)
            }
        }
      
    • 调用时机

      在 beforeCreate 之前,全局只调用一次。

    • 使用

      <template>
          <div>{{ count }} {{ object.foo }}</div>
        </template>
      
        <script>
          import { ref, reactive } from 'vue'
      
          export default {
            setup() {
              const count = ref(0)
              const object = reactive({ foo: 'bar' })
      
              // 必须return,才可以在模版上使用
              return {
                count,
                object
              }
            }
          }
        </script>
      
    • setup 函数里 this 并不是期望的组件实例,为 undefined,所以不要在 setup 访问 this,尽管这也毫无意义。

    reactive

    作用:用于实现对象数据类型的响应式侦测

    用法: 传入一个普通对象,返回值是一个经过 vue 代理过的响应式对象

    const Counter = {
        setup(){
          //reactive实现响应式,适用于一组值
          const state = reactive({
            count:0
          })
          const add =()=>{
            state.count ++
          }
          //返回一个代理后的对象,用来实现响应式
          console.log(state)
          return {
            state,
            add
          }
        },
        template:`<h1>{{state.count}}</h1><button @click="add">+</button>`,
      };
    

    ref

    作用:用于实现基础数据类型值(例:String、Number)的响应式侦测

    用法:传入一个值,返回一个 vue 内部包装过的{value:xxx}对象,并且改变 value 属性可以触发响应式更新,用于模版渲染时,不用.value 这样去访问,vue 内部会自动拆包。

    为什么这样设计?
    因为在 JavaScript 中,原始类型(例如 Number 或 String)是通过值传递的,而非引用。

    //基础类型
    let a = 1
    let b = a// a变量与b变量已没有关系,实现不了响应式
    //对象类型
    let a = {value:1}
    let b = a// a变量与b变量都在引用同一个对象,响应式就不会中断
    //ref就是vue内部帮你实现的将普通值转化为一个包装对象的工具。
    let a = ref(1)
    console.log(a) //{value:1} 值转为对象
    
    const Counter = {
        setup(){
          //ref实现响应式,适合单个值场景
          const count = ref(0)
          const add =()=>{
            count.value ++
          }
          console.log(count)
          return {
            count,
            add
          }
        },
        template:`<h1>{{count}}</h1><button @click="add">+</button>`,
      };
    

    ref 常见的.value 问题
    问题有多严重?:前端人因为 Vue3 的 Ref-sugar 提案打起来了!
    .value 到底什么时候需要
    (1) 如果自己的代码取值,需要
    (2) watch 等的 vue 自身提供的 api 上,不需要(vue 自动帮你做了拆包)
    (3) 模版取值不需要
    影响:造成开发体验上的割裂 避免:并将所有的 ref 统一命名比如:xxxRef 一定程度可以避免,或者使用 ref:语法糖。

    toRef、toRefs

    作用:reactive 返回的代理对象在组合函数的传递过程中,必须保持对返回对象的引用,以保证其响应式,该对象不能被 ES6 解构或属性拆解。
    toRef 方法

    const pos = reactive({
          x:0,
          y:0
        })
        //将响应式对象某一个属性转化为ref
        const xRef = toRef(pos,'x')
        const yRef = toRef(pos,'y')
    

    toRefs 方法

    const pos = reactive({
            x:0,
            y:0
          })
          //将整个响应式对象的全部属性转化为ref,装在一个普通对象中
          const posRefsObj = useRefs(pos)
          //等价于
          const posRefsObj = {
              x:toRef(pos,'x')
              y:toRef(pos,'y')
          }
    

    computed

    作用: 与 vue2.x 一致,根据函数体内依赖的值的变化,计算出一个新值。 用法: 传入一个计算函数,返回一个包装后的{value:xxx}响应式引用对象。

    const Counter = {
            setup(){
                const state = reactive({
                  count:0
                })
                //计算count是否是偶数,字体切换红绿颜色
                let isOdd = computed(()=>state.count%2 === 0)
                const add =()=>{
                  state.count ++
                }
                return {
                  state,
                  isOdd,
                  add
                }
              },
              template:`<h1 :style="{'color':isOdd?'red':'green'}">{{state.count}}</h1><button @click="add">+</button>`,
          };
    

    watch

    作用: 与 vue2.x 一致,主动监测响应式数据,数据改变后,执行用户传入的回调。

    const Counter = {
        setup(){
          //reactive实现响应式,适用于一组值
          const state = reactive({
            count:0
          })
          //计算count是否是奇数,字体切换红绿颜色
          let isOdd = computed(()=>state.count%2 === 0)
    
          watch(isOdd,(newValue)=>{
            alert(newValue?'偶数':"奇数")
          })
          const add =()=>{
            state.count ++
          }
          return {
            state,
            isOdd,
            add
          }
        },
        template:`<h1 :style="{'color':isOdd?'red':'green'}">{{state.count}}</h1><button @click="add">+</button>`,
      };
    

    生命周期

  • 心智负担不同
    两者都会有一定的心智负担
    react-hook 的问题是总担心频繁触发更新,useEffect 可以说是控制代码不被频繁执行最后的逃生舱,但是依赖项数组如果设置不正确,会导致副作用执行时机不正确,还可能会导致取到闭包旧值的 bug; useCallBack 经常需要使用,用来避免触发不必要的子组件渲染。 vue-compositonApi的问题刚好相反,是经常担心触发不了更新,比如解构导致的响应式丢失,vue 引入 ref 解决这个问题,但引起了总是忘记写.value 的新问题。
  • 对生命周期的看法不一样
    react-hook 有意弱化生命周期的概念,转而倡导渲染副作用的概念,由数据变化引起渲染副作用的执行。或者另一个角度说 react 用 useEffect 实现了生命周期的聚合与封装。
  • teleport (传送)
  • 组件不再限制只能存在一个根元素
  • data 选项,都统一改成函数形式(之前根组件是对象,子组件是函数)
  • $children 已废弃,只能通过 ref 获取组件及 dom 了。
  • 等等。更多的请参照官方文档
Logo

前往低代码交流专区

更多推荐