vue3.0新特性
v3新特性
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 了。
- 等等。更多的请参照官方文档
更多推荐
所有评论(0)