8

一文带你掌握Vue3新特性,再也不怕面试官啦~

 3 years ago
source link: https://my.oschina.net/u/4593078/blog/5023739
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

记录一下笔记,给各位小伙伴们分享一些常用的Vue3新特性及哪些方法发生了变更。

我这里只写一些常用的,更多请看Vue3官网这里

> 有兴趣的可以关注公众号:前端娱乐圈

组件v-model支持参数

在Vue2.x时,我们要想给子组件传值,还得单独传入。Vue3.x直接以v-model:xxx形式传入参数,并且配合修饰符.sync进行数据同步更新。

App.vue

<template>
  <div>
  {{title}} 
  <input v-model:title="title">
  </div>
</template>
<script>
import Input from "./components/Input"
export default {
  name: 'App',
  components: {
    Input
  },
  data() {
    return {
      title: "蛙人",
    }
  },
}
</script>

Input.vue

<template>
  <div class="Input">
      <input type="text" @input="first" :value="title">
  </div>
</template>
<script>
export default {
 name: 'Input',
  props: {
    title: {
        default: () => "蛙人"
    }
  },
  methods: {
      first(e) {
          this.$emit("update:title", e.target.value)
      },
  },
}
</script>

效果

组件支持多个v-model

在Vue3.x中支持在单个组件上可以创建多个v-model绑定。

App.vue

<template>
  {{title}}
  {{name}}

  <input v-model:title="title" v-model:name="name">
</template>

<script>
import Input from "./components/Input"
export default {
  name: 'App',
  components: {
    Input,
  },
  data() {
    return {
      title: "蛙人",
      name: "www"
    }
  },
}
</script>

Input.vue

<template>
  <div class="Input">
      <input type="text" @input="first" :value="title">
      <input type="text" @input="last" :value="name">
  </div>
</template>
<script>
export default {
 name: 'Input',
  props: {
    title: {
        default: () => "蛙人"
    },
    name: {
        default: () => "前端娱乐圈"
    }
  },
  methods: {
      first(e) {
          this.$emit("update:title", e.target.value)
      },
      last(e) {
          this.$emit("update:name", e.target.value)
      }
  }
}
</script>

效果

Setup

setup函数是一个新的组件选项。作为在组件内使用Composition Api的入口点。下面我们分为4个方面来讲解它

  • this指向

创建组件实例,然后初始化props,紧接着就调用setup函数。 从生命周期的角度来看,它会在beforeCreate之前执行。也就是创建组件先执行setupbeforeCreatecreate

<template>
  <div>蛙人</div>
</template>
<script>
export default {
  name: 'App',
  setup() {
    console.log("hey 蛙人")
  }
}
</script>

this指向

由于不能在setup函数中使用datamethods,为了避免使用Vue出错,所以把setup函数中this修改为了undefined

<template>
  <div>蛙人</div>
</template>
<script>
export default {
  name: 'App',
  setup() {
    console.log(this); // undefined
  }
}
</script>
  • props
  • context

props

接收组件传递过来的所有数据,并且都是响应式的。

<template>
  <div>蛙人</div>
</template>
<script>
export default {
  name: 'App',
  props: {
      title: {
          type: String
      }
  },
  setup(props) {
    console.log(props.title)
  }
}
</script>

> 注意一点,props数据不能使用解构,否则响应式数据失效

<template>
  <div>蛙人</div>
</template>
<script>
export default {
  name: 'App',
  props: {
      title: {
          type: String
      }
  },
  setup({ title }) {
    console.log(title) // 这里响应式数据将失效
  }
}
</script>

context

该参数提供一个上下文对象,从原来的2.x中选择性的暴露了一些属性。

  • attrs
  • slots
<template>
  <div>蛙人</div>
</template>
<script>
export default {
  name: 'App',
  props: {
      title: {
          type: String
      }
  },
  setup(props, { attrs, slots, emit } ) {
    console.log(attrs) 
  }
}
</script>

上面,attrsslots都是内部组件实例上对应项的代理,可以确保在更新后仍然还是最新的值。所以这里可以使用解构语法。

可以将setup函数返回值渲染到页面上。但前提是,setup返回值必须是一个对象,否则返回其它值则渲染无效。

<template>
  <div>蛙人</div>
</template>
<script>
export default {
  name: 'App',
  props: {
      title: {
          type: String
      }
  },
  setup() {
    const name = "蛙人"
    return {
       name
    }
  }
}
</script>

Reactive

该方法接收一个参数{}创建一个响应式对象。跟Vue2.x的Vue.observable一样。如果该参数不是对象的话,也可以渲染到模板上,但不是响应式的数据。

<template>
  <div class="test">
    姓名:  {{ name.value }}
    {{ test() }}
  </div>
</template>

<script>
import { reactive } from "vue"
export default {
 name: 'test',
  data() {
    return {

    }
  },
  setup() {
    let name = reactive({value: "蛙人"})
    function test() {
        name.value = "abc"; // 该方法测试响应式数据,可以看到执行完该方法视图也会发生改变
    }
    return {
        name,
        test
    }
  }
}
</script>

该方法接收一个参数,可以是单个值,也可以是一个对象,并且都是响应式的数据。当传入一个对象时{},内部将调用reactive方法进行转换为响应式数据。返回值里面带有.value属性取值,当使用模板渲染的时候可省去.value

<template>
  <div class="test">
    姓名:  {{ name }}
    {{ test() }}
  </div>
</template>

<script>
import { ref } from "vue"
export default {
 name: 'test',
  data() {
    return {

    }
  },
  setup() {

    let name = ref("蛙人")
    function test() {
        name.value = "abc"; // 只是渲染模板可以省略.value,但是在逻辑中还得写哦
    }
    return {
        name,
        test
    }
  }
}
</script>

Computed

该方法可以传入一个函数,默认该函数就是getter,不管getter返回值为一个ref响应式数据还是一个普通变量,数据都是只读不能改变。

<script>
import { ref, computed } from "vue"
export default {
 name: 'test',
 setup() {
    let name = ref("蛙人")
    let test = computed(() => name.value);
    test.value = "123" // 修改无效,只能只读
  }
}
</script>

传入一个对象setget函数方法,这样就可以修改啦

<script>
import { ref, computed } from "vue"
export default {
 name: 'test',
 setup() {
    let name = ref("蛙人")
    let test = computed({
        get() {
            return name.value;
        },
        set(val) {
            return name.value = val;
        }
    });
    test.value = "123" 
  }
}
</script>

Readonly

该方法接收传入一个对象,默认是只读功能,是深层对象只读,不管嵌套多少层的属性都是只读状态。

<script>
import { readonly } from "vue"
export default {
 name: 'test',
  setup() {
    let obj = {
        name: "蛙人",
        sex: "male",
        prodution: {
            proName: "音响"
        }
    }
    let only = readonly(obj)
    only.name = "前端娱乐圈" // 修改无效
    only.prodution.proName = "欢迎关注" // 修改无效
    console.log(only) 
  }
}
</script>

WatchEffect

该方法接收一个函数并且立即执行,并当该函数里的变量变更时,重新执行该函数。该方法无法获取到原值,只能是改变之后的值。

> 如果要监听哪个值,需要在该函数里写出来,否则监听无效

import { ref, watchEffect } from "vue"
export default {
 name: 'test',
  setup() {
    let name = ref("蛙人");
    let age = ref(23);
    watchEffect(() => {
        name.value; // 监听name
        age.value;  // 监听age
        
        console.log(name.value)
        console.log(age.value)
    })
    
    setTimeout(() => {
        name.value = "前端娱乐圈"
    }, 5000)

    setTimeout(() => {
        age.value = 18
    }, 1000)
  }
}

有时候我们想在触发一定的条件后取消监听。这时可以执行watchEffect的返回值。

import { ref, watchEffect } from "vue"
export default {
 name: 'test',
  setup() {
    let name = ref("蛙人");
    let age = ref(23);
    let stop = watchEffect(() => {
        name.value; // 监听name
        age.value;  // 监听age
        
        console.log(name.value)
        console.log(age.value)
    })
    
    setTimeout(() => {
        name.value = "前端娱乐圈"
    }, 5000)

    setTimeout(() => {
        age.value = 18;
        setTimeout(stop, 300)
    }, 1000)
  }
}

Watch

watch等同于Vue2.x中的this.$watchwatch需要监听特定数据,默认情况是懒执行,也就是只有当数据发生变化时才执行第二个参数函数。

对比WatchEffectWatch允许我们

  • 懒执行函数
  • 更明确哪些状态改变触发监听器
  • 可以监听获取到变化前后的值

监听单个值

<script>
import { ref, watch } from "vue"
export default {
 name: 'test',
  setup() {
    let name = ref("蛙人");

    watch(name, (newVal, oldVal) => {
        console.log(newVal, oldVal) // 前端娱乐圈, 蛙人
    })

    setTimeout(() => {
        name.value = "前端娱乐圈"
    }, 1000)

  }
}
</script>

监听多个值

监听多个值,返回的是一个数组对象。

<script>
import { ref, watch } from "vue"
export default {
 name: 'test',
  setup() {
    let name = ref("蛙人");
    let age = ref(23);

    watch([name, age], (newVal, oldVal) => {
        console.log(newVal, oldVal) // ["前端娱乐圈", 18], ["蛙人", 23]
    })
    
    setTimeout(() => {
        name.value = "前端娱乐圈"
        age.value = 18
    }, 1000)

  }
}
</script>

生命周期系列

在Vue3.X也可以在setup函数下使用生命周期,这些钩子函数写法跟之前的生命周期写法不同。

> 注意:这些生命周期写法只能在setup函数下使用,在其它地方使用则会报错。

与Vue2.x版本生命周期相对应的组合式Api

  • beforeCreate --> setup
  • created --> setup
  • beforeMount --> onBeforeMount
  • mounted --> onMounted
  • beforeUpdate --> onBeforeUpdate
  • updated --> onUpdated
  • beforeDestroy --> onBeforeUnmount
  • destroyed --> onUnmount

下面我们来看一下这些钩子的写法。钩子函数里面是一个回调函数。

<script>
import { onMounted, onUpdated, onUnmounted } from "vue"
export default {
 name: 'test',
  setup() {
    
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
    
  }
}
</script>

Provide && Inject

该方法和Vue2.x的 provideinject一样的。但是Vue3新特性这俩方法只能在setup中使用。

  • Provide:接收2个参数,第一个key值,第二个value值,进行传递
  • Inject:接收2个参数,第一个是providekey值,默认第二个参数可选,可以设置默认值(当找不到key值,设置一个默认值)

App.vue

<script>
import test from "./components/test"
import { provide, ref } from "vue"
export default {
  name: 'App',
  components: {
    test
  },
  setup() {
    let name = ref("蛙人")
    provide("name", name) // 传入一个响应式数据
  },
}
</script>

test.vue

<template>
	{{ NAME }}    
</template>
<script>
import { inject } from "vue"
export default {
 name: 'test',
  setup() {
    let NAME = inject("name")
    console.log(NAME) // 蛙人
      
    let title = inject("key", 123)
    console.log(title) // 这时就会触发默认值,因为这里找不到这个key值
      
    return {
        NAME
    }
  }
}
</script>

该方法相当于Vue2.x的refs一样获取元素,那么在setup中配合使用ref对象进行获取

<template>
  <div class="test">
    <p ref="el">123</p>
  </div>
</template>

<script>
import { ref, onMounted } from "vue"
export default {
 name: 'test',
  setup() {
    let el = ref(null)
    onMounted(() => {
         console.log(el) // p标签元素
    })
    return {
        el
    }
    
  }
}
</script>

isReadonly

用于检测该数据是不是可读数据。返回一个Boolean类型。

<script>
import { isReadonly, readonly} from "vue"
export default {
 name: 'test',
  setup() {
    let test = readonly({name: "蛙人"})
    console.log(isReadonly(test)) // true
      
    let test2 = readonly("蛙人")
    console.log(isReadonly(test2)) // false, 这不是一个只读数据
  }
}
</script>

isRef

用于检测该数据是不是ref响应式数据。返回一个Boolean类型。

<script>
import { isRef, ref } from "vue"
export default {
 name: 'test',
  setup() {
    let test = ref("公众号:前端娱乐圈")
    console.log(isRef(test)) // true
  }
}
</script>

isReactive

用于检测该数据是不是reacttive响应式数据。返回一个Boolean类型。

<script>
import { isReactive, reactive } from "vue"
export default {
 name: 'test',
  setup() {
    let test = reactive({name: "蛙人"})
    console.log(isReactive(test)) // true
      
    let test2 = reactive("蛙人")
    console.log(isReactive(test2)) // false, 这不是一个响应式数据
  }
}
</script>

移除过滤器filters

在Vue3.x中移除过滤器,不在支持。建议使用computed去替代。贴一个官网例子

<template>
  <h1>Bank Account Balance</h1>
  <p>{{ accountInUSD }}</p>
</template>

<script>
  export default {
    props: {
      accountBalance: {
        type: Number,
        required: true
      }
    },
    computed: {
      accountInUSD() {
        return '$' + this.accountBalance
      }
    }
  }
</script>

不再限制Template一个根节点

Vue3.x中将不在限制模板中只有一个根节点,根组件可以任意多个元素。

<template>
    <div>首页</div>
    <div>新闻</div>
</template>

自定义v-model修饰符

Vue3.x中,添加了可以自定义修饰符,如Api提供的内置方法.trim,新特性我们也可以自定义啦。下面就来演示一下写一个转换字符串大写的修饰符。

App.vue

<template>
  <input v-model:str.capitalize="modelModifiers">
</template>
<script>
import Input from "./components/Input"
export default {
  name: 'App',
  components: {
    Input
  }
}
</script>

Input.vue

<template>
  <div class="Input">
    <input type="text" @input="send">
  </div>
</template>

<script>
export default {
 name: 'Input',
  props: {
      str: String,
      strModifiers: {
          default: () => ({})
      }
  },
  methods: {
      send(e) {
          let value = e.target.value
          if (this.strModifiers.capitalize) { // capitalize 这里的值就是修饰符
            value = value.toUpperCase()
            console.log(value)
          }
          this.$emit("update:str", value)
      }
  }
}
</script>

上面方法,必须传入props值,Modifiers必须写定义为一个空对象。

> 特别需要注意一点:如果你的v-model参数值为str,那么组件里面接收的值,全部为str开头,如:props里面的 strModifiers,str

效果:

废弃on,off,once实例方法

Vue3.x中 $on$off$once 实例方法已被移除,应用实例不再实现事件触发接口。

<script>
	created() {
        console.log(this.$on, this.$once, this.$off) // undefined undefined undefined
	}
</script>

自定义指令更改

在Vue3.x中自定义指定写法稍有更改,看下列。

  • bind --> beforeMount 指令绑定到元素后发生。只发生一次
  • inserted --> mounted 元素插入到父DOM后发生
  • beforeUpdate: Vue3.x新添加的,这是在元素更新之前调用,
  • componentUpdated --> updated
  • beforeUnmount : Vue3.x新添加的,将在卸载元素前调用
  • unbind --> unmounted

main.js

import { createApp } from 'vue'
import App from './App.vue'

let main = createApp(App)
main.directive("custom", {
    beforeMount(el, attr) {
        console.log(el, attr)
    },
    updated() {
        console.log("updated")
    },
    unmounted() {
        console.log("移除")
    }
})
main.mount('#app')

App.vue

<template>
  <p v-custom v-if="show"></p>
</template>
<script>
export default {
  name: 'App',
  data() {
    return {
      show: true
    }
  },
  created() {
    setTimeout(() => {
      this.show = true;
    }, 5000)
      
    setTimeout(() => {
      this.show = false
    }, 3000)
  }
}
</script>

谢谢你读完本篇文章,希望对你能有所帮助,如有问题欢迎各位指正。

我是蛙人(✿◡‿◡),如果觉得写得可以的话,请点个赞吧❤。

感兴趣的小伙伴可以加入 [ 前端娱乐圈交流群 ] 欢迎大家一起来交流讨论

写作不易,「点赞」+「在看」+「转发」 谢谢支持❤

《聊聊在Vue项目中使用Decorator装饰器》

《分享15个Webpack实用的插件!!!》

《手把手教你写一个Vue组件发布到npm且可外链引入使用》

《分享12个Webpack中常用的Loader》

《聊聊什么是CommonJs和Es Module及它们的区别》

《这些工作中用到的JavaScript小技巧你都知道吗?》

《【建议收藏】分享一些工作中常用的Git命令及特殊问题场景怎么解决》

https://v3.cn.vuejs.org/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK