欢迎点击:个人官网博客
2020年9月份beta版本出来后就一直追3.0,作为第一批吃螃蟹的人,这里把3.0的一些知识分享一下,良心之作。喜欢就收藏吧!
目录
- 从vue2.0到vue3.0必备知识
- 一、简单介绍vue3.0(性能比2.x快1.2~2倍)
- 二、常用Composition API
- 三、setup语法糖,<script lang=“ts“ setup>
- 四、比较Vue2与Vue3的响应式
- 五、vue3使用vuex,vue-router
- 六、简单总结
从vue2.0到vue3.0必备知识
一、简单介绍vue3.0(性能比2.x快1.2~2倍)
Vue3支持大多数的Vue2的特性。如果3.0项目中2.0跟3.0混搭写法,方法重名的话3.0优先
Vue3中设计了一套强大的组合APi(Compostion API)代替了Vue2中的option API ,复用性更强了。可自定义hook函数,不需要代码都写在一个template(再也不怕代码多起来,滚轮滚到手发抖了)
按需编译,体积比Vue2.x更小
Vue3更好的支持TypeScript(TS)
Vue3中使用了Proxy配合Reflect 代替了Vue2中Object.defineProperty()方法实现数据的响应式(数据代理)
重写了虚拟DOM,速度更快了
新的组件: Fragment(片段) / Teleport(瞬移) / Suspense(不确定)
设计了一个新的脚手架工具—vite,npm run dev 秒开,热重载也很快。这种开发体验真是很爽,拒绝等待。
全局 API 现在只能作为 ES 模块构建的命名导出进行访问
Vue3可多个根组件
Vue3中v-if优先于v-for,移除过滤器
二、常用Composition API
import{
defineComponent,//目的是定义一个组件,vue3如果用ts,导出时候要用 defineComponent,这俩是配对的,为了类型的审查正确
defineAsyncComponent,//vue3中动态引入组件
provide,//祖孙组件传参
inject,//祖孙组件传参
reactive,//定义多个数据的响应式,可配合torefs使用方便
toRefs,//torefs函数可以将reavtive创建的响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref,html模板中直接使用变量显示数据
ref,//定义一个数据的响应式(一般是基本数据类型),通过.value获取到值,如果是object之类会自动转化为reactive
computed,//计算属性,如果只传入一个回调函数,表示的是get,可get,set
watch,//监听一个或多个属性或对象,默认不执行---可加参数immediate表示默认执行一次,deep表示深度监视
watchEffect,//不需要配置immediate,默认就会执行一次。监视所有回调中使用的数据
onMounted,//生命周期
onUnmounted,
onUpdated,
onBeforeUpdate,
onBeforeMount,
onBeforeUnmount,
nextTick,//整个视图都渲染完毕后
customRef,//追踪数据并告诉Vue去触发界面更新,可用于对页面更新进行一个防抖
readonly,//数据只读
shallowReactive,// 只处理了对象内最外层属性的响应式(也就是浅响应式)
shallowRef,//只处理了value的响应式, 不进行对象的reactive处理
toRaw,//返回由 reactive 或 readonly 方法转换成响应式代理的普通对象。}from"vue";
1.setup(props,context)
setup是组合api的入口函数,函数返回对象,html模板中可以直接使用。
setup细节问题:
1.setup在beforeCreate之前执行。
由此推断setup在执行的时候,当前组件还没有创建出来,也意味着组件实例对象this根本不能用(undefined)。
2.setup中的返回值是一个对象,内部的属性和方法是给html模板使用的。
- props即为父组件传递过来的值,当你在steup函数中获取值不能像vue2.0中this.值,是获取不到的,要props.值
- context上下文对象,里面有attrs,slots,emit
import{defineComponent}from"vue";exportdefaultdefineComponent({setup(props,{attrs,slots,emit}){
console.log("this=>",this);//undefined
console.log(props);//父组件传过来是值
console.log(slots);//插槽
console.log(attrs);//当前组件标签上的属性,不带冒号的,带冒号是props//如<Child :msg="count" text="我是src" /> text是attrs,mag是props---------------------constaddCount=()=>{emit("demoHander","我的子组件传来的");//向父组件传参,这种写法父组件@demoHander='函数'//或者
props.demoHander('我的子组件传来的')//这种写法父组件:demoHander='函数'};return{};//必须为一个对象}});
2.ref,reactive
<template><div>{{ count}}{{state.age}}<button @click="add">加</button></div></template><script lang="ts">import{ defineComponent, ref, reactive}from"vue";exportdefaultdefineComponent({setup(){// let count = 0;//这个数据不是响应式数据,(响应式数据:数据变化,页面跟着渲染)let count=ref(0);//ref一般定义一个数据的响应式(一般是基本数据类型,单独定义某一个常量),方法中要通过count.value获取到值,如果ref是一个对象也可以,会自动转为reactive---------------------------------------------let obj={
name:"tom",
age:25,
wife:{
name:"marry",
age:22,},};let state=reactive(obj);//reactive一般定义多个数据(复杂数据类型)的响应式,如果不使用torefs的话,html模板中要使用state.age显示数据//方法constadd=()=>{
count.value++;//操作ref定义的值
state.age++;//操作reactive定义的值------------------------// obj.age++;这种方式页面不会更新渲染,需要操作代理对象才有用// delete state.wife//删除某个属性,页面会更新};return{
count,
add,
state//响应式数据//...state 不是响应式数据//...toRefs(state) 响应式数据};},});</script>
reactive不能处理简单数据类型。如reactive(10)是无法响应式的,必须是一个对象reactive({ })
ref 是一个对象也可以,会自动转为reactive。如ref({ id:10 })
注意: 解构state 或者 扩展运算符都会使数据失去响应性
reactive改用toRefs配合使用
toRefs()函数可以将reactive创建出来的响应式对象,转换为普通对象,只不过这个对象上的每个属性节点,都是ref()类型的响应式数据
(使用…操作符返回的对象,不会失去响应性)
<template><div><!-- 不使用toRefs:{{state.age}}--><!--使用toRefs-->{{ age}}</div></template><script lang="ts">import{ defineComponent,reactive,toRefs}from"vue";exportdefaultdefineComponent({setup(){let obj={
name:"tom",
age:25,
wife:{
name:"marry",
age:22,},};let state=reactive(obj);return{//state...toRefs(state)};},});</script>
ref获取元素
<template><div><input type="text" ref="inputRef" name="" id=""/></div></template><script lang="ts">import{ defineComponent, ref,}from"vue";exportdefaultdefineComponent({setup(){//通过ref自动获取焦点const inputRef= ref<HTMLElement|null>(null);onMounted(()=>{
inputRef.value&& inputRef.value.focus();});return{
inputRef,};},});</script>
如果项目中方法比较多时,可以这样处理,方便看
3.watch,watchEffect
watch监听一个属性或对象
setup(){let state=reactive({
fristName:"东方",
laetName:"不败",
fallName3:""});------------方法一watch(state,({fristName,laetName})=>{
fallName3.value=fristName+'_'+laetName},{immediate:true,deep:true})//immediate表示默认执行一次//deep表示深度监视------------方法二const stop=watch(()=> state.fristName,(val,oldval)=>{
console.log(val);},{deep:true});//直接调用stop(),可以清除监听return{...toRefs(state)};}
watch监听多个属性
//监听reactive创造出来的数据使用箭头函数,ref直接放
watch([() => state.fristName, () => state.laetName, fallName3], (val) => {
console.log(val);
});
watchEffect
//watchEffect不需要配置immediate,默认就会执行一次// watchEffect: 监视所有回调中使用的数据watchEffect(()=>{
fallName3.value= state.fristName+'_'+state.laetName})
vue2.0写法
watch:{/**
* 监听路由变化刷新页面
*/$route(to){
logger.log("watch router invoke", to.query);this.initPage();},
configureList:{handler(newVale){
logger.log("watch configureList invoke", newVale);},
deep:true,
immediate:false,},},
4.computed
注意:计算属性如果只传入一个回调函数,表示的是get。
场景:三个input任意一个值变化,其他两个也会变化
<input type="text" v-model="state.fristName"/><input type="text" v-model="state.laetName"/><input type="text" v-model="fallName"/>
计算属性如果只传入一个回调函数,表示的是get。即fristName或laetName改变,fallName发生改变。但是fallName发生改变,其他两个值不会变化
setup(){let state=reactive({
fristName:"东方",
laetName:"不败",});//fallName 返回的是一个ref对象const fallName=computed(()=>{return state.fristName+"_"+ state.laetName;});}
解决办法:
computed有不光有get,还有set
const fallName=computed({get(){return state.fristName+"_"+ state.laetName;},set(val: any){const names= val.split("_");
state.fristName= names[0];
state.laetName= names[1];},});
5.生命周期
6. provide 和 inject(实现跨层级组件(祖孙)间通信),也可放在main.ts里面进行全局挂载使用
父组件
<template><h1>父组件</h1><p>当前颜色:{{color}}</p><Son/></template><script lang="ts">import{ provide, ref}from'vue'import Sonfrom'./Son.vue'exportdefault{
name:'ProvideInject',
components:{
Son},setup(){const color=ref('red')provide('color', color)return{
color}}}</script>
子组件
<template><h2>子组件</h2><GrandSon/></template><script lang="ts">import GrandSonfrom'./GrandSon.vue'exportdefault{
components:{
GrandSon},}</script>
孙组件
<template><h3:style="{color}">孙子组件:{{color}}</h3></template><script lang="ts">import{ inject}from'vue'exportdefault{setup(){const color=inject('color')return{
color}}}</script>
7.自定义hook函数(即把函数抽出来复用)
抽离前:
<template><button @click="add">add</button><img:src="src" alt=""></template><script>import{reactive, toRefs}from"vue";exportdefault{setup(){const state=reactive({
index:1,
src:''});constadd=()=>{
console.log(state.index)
state.index++;
console.log(state.index)};return{...toRefs(state),
add};},};
抽离后:
<template><button @click="add">add</button><img:src="src" alt=""></template><script>import handerfrom'./hooks/demo.ts'import{toRefs}from"vue";exportdefault{setup(){let{state,add}=hander()return{...toRefs(state),
add};},};
demo.ts
import{reactive}from"vue";exportdefaultfunctionuseMousePosition(){const state=reactive({
index:1,
src:''});constadd=()=>{
console.log(state.index)
state.index++;
console.log(state.index)};return{state,add}}
8.defineAsyncComponent配合Suspense异步组件使用
<script lang="ts">import{ defineComponent, defineAsyncComponent}from"vue";// vue正常引入组件import SuspensChildfrom'@/components/Suspense/Son.vue'// vue2中动态引入组件的写法:(在vue3中这种写法不行)constAsyncCompotent=()=>import('./Son.vue')//vue3中动态引入组件const SuspensChild=defineAsyncComponent(()=>import("./Son.vue"));</script>
注意:由于组件是异步引入的,会有一瞬间的空白,所以可以用Suspense配合loading填充,可以让我们创建一个平滑的用户体验
<template><Suspense>//v-slot:default也可以写成#default<template v-slot:default><!-- 组件加载完后显示--><SuspensChild/><!-- 异步组件--></template><template v-slot:fallback><!-- 组件加载完前显示--><h1>LOADING...</h1><!--LOADING的内容--></template></Suspense></template><script lang="ts">import{ defineComponent, defineAsyncComponent}from"vue";const Sus