Vue总结

226 阅读5分钟

简介

  • Vue2 + 修饰器模式【支持TS】需要安装以下插件
  • Vue2 + 修饰器模式其实是向Vue3过渡的版本

npm i -S vue-property-decorator

npm install --save vue-class-component

  • 修饰写法告警

image.png

  • 解决

image.png

搜素:experimentalDecorators image.png

main.js 文件的区别

Vue2

image.png

Vue2 + 装饰器

image.png

VUe3

  • 一改之前的new Vue 和 render 方法。通过引入createApp代替
  • 支持链调用

image.png

模板的区别

Vue3 取消了跟标签的限制

Vue2

image.png

Vue2 + 装饰器

image.png

VUe3

  • Vue3 将组建标签都放到了内置的Fragment组件中,减少嵌套 image.png

组件暴露

Vue2

<script>
export default {
  name: 'app',
 }
</script>

Vue2 + 装饰器

<script>
import {Vue} from 'vue-property-decorator'

export default class Home extends Vue  {
 
  
}
</script>

VUe3

<script>
export default {
  name: "App",
 }
 </script>

组件名

Vue2

<script>
export default {
  name: 'app',
 }
 </script>

Vue2 + 装饰器

<script>
import {Vue,Component} from 'vue-property-decorator' // 要引入 Component
@Component({ // 注意书写位置不在组件配置里
  name:'app'
})
export default class Home extends Vue  {

}
</script>

VUe3

<script>
export default {
  name: "App",
};
</script>

操作属性的舞台

Vue2

<script>
export default {
  name: 'app',
  components:{},
  props:{},
  data(){
  return {}
  },
  computed: {},
  created() {},
  mounted() {},
  methods: {},
}
</script>

Vue2 + 装饰器

<script>
import {Component, Vue} from 'vue-property-decorator'
@Component({
  name:'app'
})
export default class Home extends Vue  {
  // 直接操作
  
}
</script>

VUe3

<script>
export default {
  name: "App",
  setup(){
    // 在setUp方法中操作

    return {
      
    }
  }
};
</script>

组件中的data【响应式数据】

Vue2

<script>
export default {
  name: 'app',
  data(){ // data中的数据就是响应式数据
  return {
    name:'小明'
  }
  },

}
</script>

Vue2 + 装饰器

<script>
import {Vue,Component} from 'vue-property-decorator' // 要引入 Component
@Component({ 
  name:'app'
})
export default class Home extends Vue  {
  name = '小明' // 直接定义即可且为响应式
  
}
</script>

Vue3

基本数据类型变成响应式数据

方法:

ref()

特点:

  • 模板直接使用
  • setUp()内使用,因为它做了包装,所以要用 xxx.value 形式获取 image.png
export default {
  name: 'home',
  setup(props,{attrs,slots,emit}){
    let id = ref(1003) 
    return {
      id,
    }
  }
}

响应式基本数据类型变成非响应式

  • unref():是 val = isRef(val) ? val.value : val 的语法糖。
  • 当发送请求时,我们不希望数据被包装过所以要用到该方法

unref()

复合数据类型变成响应式数据[深度响应式(proxy)]

注意点:

  • 页面模板只用通过对象点属性的形式去拿取相应的数据
  • reactive()定义的响应式对象setUp中获取该对象无需进行 .value 获取。可直接使用
  • ref()定义的响应式对象setUp中获取该对象需进行.value获取

ref() // 当传入对象时代用的还是reactive()

image.png

reactive() // 只收对象形式否则会报错且能成为响应式数据

image.png image.png

响应式复合数据类型变为非响应式

ref()定义的

toRaw(userInfo.value)

reactiv()定义的

toRaw(userInfo)

image.png

抛出对象类型的响应式数据【方便模板取值】

toRef(Object,key)

  setup(props, { attrs, slots, emit }) {
    let userInfo = reactive({
      name: "迪丽热巴",
      age: 18,
    });

    return {
      name:toRef(userInfo,'name'),
      age:toRef(userInfo,'age'),
      handler,
    };
  },

image.png

toRefs(object) // toRef()升级版 toRefs()返回值,是一个对象形式的代理对象 image.png

  setup(props, { attrs, slots, emit }) {
    let userInfo = reactive({
      name: "迪丽热巴",
      age: 18,
    });
    return {
      ...toRefs(userInfo),
    };
  },

只读的响应式对象数据

readonly(Object)

自定义的响应式函数

  • 可以定义特殊的响应式

customRef()

<input v-model="text" />

function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      }
    }
  })
}

export default {
  setup() {
    return {
      text: useDebouncedRef('hello'300) // 数据改变后300ms才渲染页面
    }
  }
}

组件中的methods

Vue2

  methods:{
    click(){
      this.obj.name.test = '30'
    }
  },

Vue2 + 装饰器

export default class Home extends Vue  {
  name = '小明'
  click(){
    this.name = '小白'
  }
}

Vue3

  setup(props, { attrs, slots, emit }) {
    function handler() {
      userInfo.name = '贾玲'
    }
    return {
      handler, // 一定要吐出来才能用
    };
  },

组件中的computed

Vue2

简便写法

  computed:{ // 当修改该值时无法修改
    price(){
      return this.age
    }
  },

可修改的写法

  computed:{
    price:{
      get(){
        return this.age
      },
      set(value){
        this.age = value
      }
    }
  },

Vue2 + 装饰器

export default class Home extends Vue  {
  age = 20
  get price(){
    return this.age
  }
  set price(value){
    this.age = value
  }
}

Vue3

简便写法

  setup(props, { attrs, slots, emit }) {
    let age = ref(20)
    let price = computed(()=>{
      return age.value
    })
    return {
      price,
    };
  },

可修改的写法

  setup(props, { attrs, slots, emit }) {
    let age = ref(20)
    let price = computed({
      get(){
        
        return age.value
    
      },
      set(value){
        age.value = value
      }
    })
    return {
      price,
    };
  },

组件中的Watch

Vue2

基本数据类型监视

  watch:{
    age(newData,oldData){

    }
  },

复合数据类型监视

  watch:{
    userInfo:{
      immediate:true,
      deep:true,
      handler(newData,oldData){

      }
    }
  },

Vue2 + 装饰器

  @Watch('info',{immediate:true,deep:true}) handler(newData,oldData){
    console.log(newData)
  }

Vue3

  • Vue3 reactive()代理的对象进行监视时,新旧数据都为新的数据【Vue3 BUG】
  • 当要监视某个对象的某个属性的属性值扔是对象时,需要开始深度监视。否则所有的深度监视是无效的。
  • ref()包裹的对象,监视时需要使用 .value 且要开启深度监视

单个监视对象

    watch(userInfo,()=>{
      console.log('userInfo');
      
    },{
      immediate:true,
      deep:true, // Vue3 默认开启的是深度监视
    })

多个监视对象

    watch([userInfo,carInfo],(newData,oldData)=>{
      console.log(newData,oldData);
      
    },{
      immediate:true,
      deep:true, // Vue3 默认开启的是深度监视
    })

image.png

监视某一个对象的某一个属性【利用函数返回】


    watch(userInfo.age,(newData,oldData)=>{ // 无效【错误写法】
      console.log(newData,oldData);
    })
    watch(() => userInfo.age,(newData,oldData)=>{ // 正确
      console.log(newData,oldData);
    })

监视某个对象中的某个属性的属性值仍然是对象【要开深度监视】

    watch(() => userInfo.desc,(newData,oldData)=>{ 
      console.log(newData,oldData);
    },{deep:true})

升级版的监视【watchEffect】

  • 当依赖的数据发生变化,就执行回调。【类似于计算属性】
    watchEffect(() => {
      let num = userInfo.age
      console.log('执行了')
    })

组件中的props

Vue2

组件内定义接收的props

    props:['age']
    
    props:{
    age:{
      type:Number,
      require:true,
      default:() => (20)
    }
  },

Vue2 + 装饰器

@Prop([Number]) age

@Prop({type:Number,required:true,default:() => 20}) age

Vue3

  • 定义
    props:['age']
    
    props:{
    age:{
      type:Number,
      require:true,
      default:() => (20)
    }
  },
  • 使用
export default {
  name: "home",
  props:{
    age:{
      type:Number,
      require:true,
      default:() => (20)
    }
  },
  setup(props, { attrs, slots, emit }) { // setUp函数接收到两个参(props,context)
    console.log(props.age) // props 中存放了定义的props
  },
};

组件中的emit

Vue2

handler(){
        this.$emit('showBtn',false)
}

Vue2 + 装饰器

  • 使用 @Emit() 方法,该方法中传入的事件名就是emit名字
  • @Emit() 方法 后紧随的方法是触发emit的方法,且必须传入值,并将该值返回
// 子组件 
<template>
  <div>
    <button @click="showBtn(false)">emit</button>
  </div>
</template>

<script>
import {Component, Emit, Vue,} from 'vue-property-decorator'
export default class Home extends Vue {
  @Emit('showBtn') showBtn(a){return a} // 必须传入参数值否则报错
}

// 父组件使用
<Home @showBtn="handler"></Home>

Vue3

  setup(props, { attrs, slots, emit }) {
    function handler(){
      emit('showBtn',false)
    }
    return {
      handler
    }
  },

组件中的ref

Vue2

  <Home ref="home"></Home>
  handler(){
      this.$refs.home
  }

Vue2 + 装饰器

  <Home ref="home"></Home>
  handler(){
      this.$refs.home
  }

Vue3

  • 子父组件中借助于 getCurrentInstance()方法获取组件实例
  • 组件实例身上有ref属性存放着打了ref则组件
  setup(props,context){
    const vm = getCurrentInstance(); //获取当前组件实例
    function handler(flg) {
      vm.refs.home.change()
    }
    return {
      handler
    }
  }

组件中的slots

Vue2

  • 组件内部传入的标签会默认放置到$slots上,没有名字则默认为default

image.png

  • 传入名称的插槽后也会对应生产相应名称的插槽

image.png

    子组件中的 this.$slots 属性

Vue2 + 装饰器

  • 组件内部传入的标签会默认放置到$slots上,没有名字则默认为default

image.png

  • 传入名称的插槽后也会对应生产相应名称的插槽

image.png

    子组件中的 this.$slots 属性

Vue3

  • 放置在setUp()参数中的 sloat
  • 命名插槽要使用 v-slot:xxx
// 使用插槽
  <Home>
    <template v-slot:title>sdfsfsfdsfs</template>  // v-slot:title [只有该属性生效]
  </Home>
// 创建插槽
<slot name="title"></slot>
// 获取插槽信息
  setup(props, { attrs, slots, emit }) {
    console.log(slots)
   }

image.png

mixin

Vue2

export default {
  name: 'home',
  mixins:[form],
  }

Vue2 + 装饰器

@Component({ 
  name:'app',
  components:{Home},
  mixins:[from]
})

Vue3

  • hook函数

创建hook函数

image.png

使用hook函数

image.png

生命周期不同

Vue2

beforeCreate: function() {
        alert('Before Create');
    },
created: function() {
        alert('Created');
    },
beforeMount: function() {
        alert('Before Mount');
    },
mounted: function() {
        alert('Mounted');
    },
beforeUpdate: function() {
        alert('Before Update');
    },
updated: function() {
        alert('Updated');
    },
beforeDestroy: function() {
        alert('Before Destroy');
    },
destroyed: function() {
        alert('Destroyed');
    }

Vue3

setUp() 外书写的生命周期

  • 对标Vue2只有 beforeDestroy 改为 beforeUnMount()
  • destroyed 改为 unMounted()
  • setUp() 在 beforeCreate() 前创建所以在setup中没有this

setUp() 中的生命周期

  • setUp() 中没有beforeCreate,created
  • setUp() 中的生命周期构造都是带On的
  • setUp() 中的生命周期钩子使用的是回调形式的
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onRenderTracked,
  onRenderTriggered,
   onMounted(() => { // 回调形式
      console.log(obj)
    })

内置的组建

Vue2

component [动态渲染组件]

// is 可以是 组件名称或组件实例
<component v-bind:is="currentTabComponent"></component>

transition

<transition> <div v-if="ok">toggled content</div> </transition>

transition-group

<transition-group tag="ul" name="slide"> <li v-for="item in items" :key="item.id"> {{ item.text }} </li> </transition-group>

keep-alive

  • props : includes(包含)[字符串或正则或字符串数组] exclude(不包含)[字符串或正则或字符串数组]
<keep-alive> <component :is="view"></component> </keep-alive>

slot

Vue3

Teleport[传送门组件]

  • props : to
<teleport to="#some-id" />
<teleport to=".some-class" />
<teleport to="[data-teleport]" />

Suspence [异步组件]

  • 引入异步组件
  • 利用Suspence组件处理异步组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
<template>
	<div class="app">
		<h3>我是App组件</h3>
		<Suspense>
			<template v-slot:default>
				<Child/>
			</template>
			<template v-slot:fallback>
				<h3>加载中.....</h3>
			</template>
		</Suspense>
	</div>
</template>