为什么升级 vue3
我们咬文嚼字地从中提炼一些信息:
- “能用vue3却还在用Options API”--说明vue3+ 并非颠覆性版本,依然支持原来Options的写法,vue3兼容大部分vue2的写法(除了废弃的-都是不常用)不过,既然要升级,建议不要混着使用
2."现在有了<script setup>没有理由不换"--setup 至关重要,并且有两种写法,“现在有了”是指vue3.2才有的<script setup>写法,vue3.2之前setup写法不同
3.“不换Composition API”-- 写法上将Option API替换成 Composition API,也就是不在像上图那样,data中定义属性数据,methods中定义方法,computed定义计算属性,watch中些监听等等共同处理页面逻辑,初写vue2时觉得这种分代码块的写法很好,分块管理代码,但慢慢的我发现,同一个功能的代码混在不同的代码块中,每次我想要修改或新增一个功能都要来来回回的在几个代码块中跳来跳去,如果业务复杂,页面很长,这种跳来跳去的找代码,太难受啦!! 当然你可以用mixins 提取功能,但mixins有它的弊端,比如,命名冲突,因为mixins的引起方式不能清晰的看到抽离的mixins中有哪些变量,方法等,还有其他的弊端,所以有人说远离mixins。 试想如果我们同一个功能的代码都在一起,维护起来是不是简单一些,当然一个页面有可能混着多个功能,如果我们能把功能提取出来独立的js,并且在这个页面引入时能清晰的看到引入的多个模块中有哪些变量,方法,函数等等,这样是不是也很香呢,vue3 可以做到。
vue3在原理实现上有变化,性能方面也有很大的提升,建议参考 面试官:Vue3.0的设计目标是什么?做了哪些优化?_动感超人,的博客-CSDN博客
Compositions API
以下为<3.2 版本写法。组合式API写法的入口是setup,示例代码如下:
<script>
import { ref, reactive, toRefs, computed} from 'vue'
export default {
props: ['info'],
setup(props,content) {
console.log('setup执行了'); /* 此时组件还未创建,this不可用 */
console.log(this)
console.log(props.info) /* 接收父组件传参‘HelloWorld’ */
const color = ref("red"); /* ref用于定义一个响应式的数据,返回的是一个Ref对象,对象中有一个value属性 */
const state = reactive({ /*reactive用于创建对象或数组类型的响应式对象*/
count: 0,
age: 1
})
const name = 'Jim' /* 非响应的,也就是 模板中显示{{name}},当js中我们改变name的值时,页面不会刷新为最新值*/
const multiple = computed(() => { /* 定义计算属性*/
return 2 * state.count
// return color.value /* 如果需要对数据进行操作,需要使用该Ref对象的value属性 */
})
const increase = () => { /* 定义方法 */
state.count++;
console.log(multiple.value)
};
// const {count} = state /* {{count}}可被渲染初始值,但解构后失去响应式 */
return { /* 注意:一定要在setup的return中返回,不然会报错。*/
...toRefs(state), /* {{count}} 解构并保持响应式 (用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象)*/
// state, /* {{state.count}} */
multiple,
color,
increase,
}
},
beforeCreate(){
console.log('beforeCreate执行了');
}
}
在setup内都做了什么,定义了数据, 定义了方法, 定义了计算属性, 也可以定义watch监听等等
注意:所有这些定义必须return,这是setup选项式写法必须的
return { /* 注意:一定要在setup的return中返回,不然会报错。*/
...toRefs(state), /* {{count}} 解构并保持响应式 (用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象)*/
// state, /* {{state.count}} */
multiple,
color,
increase,
}
注意:vue3要显示的引用,这样做的好处是按需引入,有利于性能优化(tree shaking可以摇掉不需要的模块,减小项目体积)
import { ref, reactive, toRefs, computed} from 'vue'
注意:因为setup执行晚于beforeCreate,组件还没有创建,因此没有this,如果想拿到props,不能通过this,所以vue3设计了setup有两个参数,第一个就是props,如代码中,这样就能拿到props了,另外一个参数详见文档组合式 API | Vue.js (vuejs.org)
console.log(props.info) /* 接收父组件传参‘HelloWorld’ */
组合式API,就是把这些组合在一起写,试想如果这些就是实现某一个功能,我们把代码放在一起,当我们需要维护时,在这一段代码中就可以全部找到。
Reactivity API
上面的代码中,定义数据同vue2不同,这就 涉及到了响应式API(参照 响应性 API | Vue.js (vuejs.org))
定义响应式数据使用ref 和 reacitve,如下:
ref需要注意的,js需要对ref定义的数据进行操作必须用 变量.value,例如下面的color,必须color.value
reactive创建的对象可以使用 toRefs()进行解构赋值
这样就可以在template模板中直接使用state内部的count和age{{count}}{{age}},不需要再写{{state.count}}
const color = ref("red"); /* ref用于定义一个响应式的数据,返回的是一个Ref对象,对象中有一个value属性
*/
const state = reactive({ count: 0, age: 1 }) /*reactive用于创建对象或数组类型的响应式对象*/
它的好处如下:
-
降低组件实例化时间
vue3.0以前,组件实例在初始化的时候会将data整个对象变为可观察对象,通过递归的方式给每个Key使用Object.defineProperty加上getter和setter,如果是数组就重写数组对象的7个方法;而在vue3中将创建可响应对象的权利交给开发者,开发者可以通过ref,reactive,computed,watch,effect等方法自定义自己需要响应能力的数据,实例在初始化时不需要再去递归data对象了,从而降低组件实例化的时间
-
降低运行内存
vue3之前生成响应式对象会对对象进行深度遍历,同时为每个Key生成一个def对象用来保存Key的所有依赖,当key对应的value发生变化时通知依赖项进行updata,但如果这些依赖项在页面整个生命周期内不需要更新的时候,这时def对象收集的这些依赖不仅没有还会占用内存,如果可以在初始化时忽略这些不会变化的值就好了。vue3解决了这个痛点,可以通过ref,reactive,开发者可以选择性的创建可观察对象,达到减少依赖项的保存,降低运行内存的使用。
计算属性,监听用法同vue2一样,写法上略有差别,通过安装vscode插件 Vue VSCode Snippets,实现快捷写法,比如输入v3,插件会提示你,选中一个,回车,基础写法就有了,不需要记忆的,用多了自然就记住了
以上是组合式API基础写法,vue3.2升级简化了setup写法,参看单文件组件
<script lang="ts" setup>
import { ref } from 'vue'
// let color = 'red'; //非响应式
let color = ref('red');//响应式
let count = ref(0); // ref用于定义一个响应式的数据
const increase = () => {
count.value++; // 如果需要对数据进行操作,需要使用value属性
// color = 'green'; //无效
color.value = 'green'; //必须使用.value 文字颜色改变
};
</script>
<script setup>写法简化了setup选项写法,
不需要export了直接在标签内写;
不再需要return;
引用的子组件,不需要注册了,import引入即可直接在模板中使用 <Test/>;
不写setup(props,content)了,那我们操作props怎么办呢,vue3.2提供了专属API实现,参照
最后: 在setup中使用生命周期函数同vue2中不同,setup有专属的生命周期钩子,以下为选项式API钩子同setup 钩子对比,用法一样。