Vue3

拉开序幕的 setup

  • setup 是所有 composition API 的表演舞台。组件中所用到的数据、方法等均配置在 setup 中。
  • setup 函数可以返回两种东西
    1. 返回一个对象,对象中的属性、方法在模板中均可直接使用
    2. 可以返回一个渲染函数 render函数,可以自定义渲染内容。
  • vue3 的 compositionAPI 不要与 vue2 的 optionAPI 的数据混用。vue2能访问到vue3的数据,vue3不能访问vue2,具体见生命周期。
<script>
import {ref} from 'vue'
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  setup() {
    let a = ref(0);
    const b = ref(2);
    const fn = ()=>{
      console.log(a.value++)
    }
    return {
      a,b,fn
    }
  },
};
</script>

ref 和 ref返回值的value

数据需要用 ref 函数包装成 RefImpl 的实例才能实现响应式,使用 ref 需在 vue3 库中 import 一下 ref,修改 ref 值需要修改他的 value 而不是直接改,否则报错 error Must use '.value' to read or write the value wrapped by 'ref()',而模板中读取 ref 数据则不需要 .value

<template>
    <p> a </p>
    <button @click="fn">++</button>
<template>

<script>
import {ref} from 'vue'
export default {
  setup() {
    let a = ref(0);
    const fn = ()=>{
      console.log(a.value++)
    }
    return {
      a,b,fn
    }
  },
};
</script>

如果 ref 的参数是一个对象,那么读取或修改对象里的值就不用 .value,控制台输出这个b的值可以看到是一个proxy的实例,借助了vue3的性函数 reactive 函数实现的

const b = ref({
    b1: 'bbbbb',
    b2: 22222
}) 
;(function(){
    console.log(b.value);
    b.value.b2++
})()

reactive

上面说到 ref 传入复杂数据类型会借助 reactive 这个函数,往 reative 传入基础类型数据则会警告说 value cannot be made reactive,使用 reactive 包装复杂类型为响应式数据,修改对象里面的值,就不用 .value了。

const b = reactive({
    b1: 'bbbbb',
    b2: 22222
}) 
;(function(){
    console.log(b.value);       //输出undefined,因为没有b变成一个proxy对象了
    b.b2++
})()

proxy响应式原理例子

const person = {
    name: 'ming',
    age:18
}
const p = new Proxy(person,{
    get(target,propName){
        console.log(`有人读取了p身上的${propName}属性`);
        return Reflect.get(target,propName)
    },
    set(target,propName,value){
        console.log(`有人修改或增加了p身上的${propName}属性`);
        Reflect.set(target,propName,value)
    },
    deleteProperty(target,propName){
        console.log(`有人删除了p身上的${propName}属性`);
        return Reflect.deleteProperty(target,propName)
    }
})

setup注意事项

setup执行时机

setup 会在 beforeCreate 之前执行一次,this 指向为 undefined

setup的参数 props 和 context

setup(props,context){
    console.log(props,context)
}

props 和 context.attrs

  • 第一个参数 props 是父组件传递过来的数据,context.attrs 类似于vue2里的 $attrs ,是未被props接收的组件通信的数据。
  • 在低版本的vue3中,如果传递了 props而不在子组件中使用 props 接收,则会发出警告。所以推荐传递的 props 都用 props 接收。

context

输出 context,可以看到熟悉的有 attrs,emit,slots

emit:如果父组件给子组件绑定了自定义事件,如果子组件想要触发自定义事件,则需要 context.emit() 触发,不再像vue2那样$emit触发

slots:就是插槽相关内容。

computed

类似于 vue2 中的 计算属性,使用时需要引入 computed ,computed函数里面的参数:如果是简写形式是一个函数,如果是完整形式是一个对象

import {computed} from 'vue'

setup(){
    let fullName = computed(()=>{
        return person.firstName + person.lastName
    })
}

完整形式

setup() {
    const person = reactive({
        firstName: '',
        lastName: ''
    })
    let fullName = computed({
        get() {
            return person.firstName + person.lastName
        },
        set(value) {
            person.firstName = value.slice(0, 1)
            person.lastName = value.slice(1)
        }
    })
    return {
        person, fullName
    }
}

watch

vue3中 watch 函数监视属性。

  1. 第一个参数为监视的目标,可以是一个ref,也可以是一个数组里包多个ref。也可以是 reactive,注意 ref 不是 ref.value
  2. 第二个参数是一个回调函数,回调函数里包含两个参数 oldValue 和 newValue
  3. 第三个参数是配置项,用于开启 immediate 和 deep
<template>
    <h2>{{ auro }}</h2>
    <button @click="auro += 'o'"></button>
</template>
<script>
import { ref, watch } from 'vue'
export default {
    setup() {
        let auro = ref('auro');
        let panton = ref('panton')
        watch([auro,panton], (newValue, oldValue) => {
            console.log(oldValue, newValue);
        })
        return { auro }
    }
}

watch监视对象的三种情况

监视一个对象

但vue3中监视 reactive 类型数据自动开启深度监视且关不掉。
同时注意监视 reactive 类型数据无法监测 newValue 和 oldValue,因为是复杂类型数据,改了之后新旧值是一样的。

监视对象里的属性

监视对象里的属性(基础数据类型),监视的目标要写成 函数有返回值 形式,这样子可以监视得到newValue 和 oldValue。
当然也可以是一个数组里包多个函数

export default {
    setup() {
        let bibu = reactive({
            name: 'bibu',
            age: 22
        })
        watch(() => bibu.age, (newValue, oldValue) => {
            console.log(oldValue, newValue);
        })
        
        watch([() => bibu.age,()=>biubiu.name], (newValue, oldValue) => {
            console.log(oldValue, newValue);
        })
        return { auro, bibu }
    }
}

监视对象里的属性(复杂数据类型),监视目标写成 函数有返回值 形式以外,还要开启 deep:true 配置项才能监视得到改变进而使用watch里的回调函数。

export default {
    setup() {
        let bibu = reactive({
            name: 'bibu',
            age: 22,
            hobby: ['抽烟', '喝酒']
        })
        watch(() => bibu.hobby, (newValue, oldValue) => {
            console.log(oldValue, newValue);
            console.log(bibu.hobby);
        }, { deep: true })
        return {bibu}
    }
}

监视 ref 定义的对象

监视ref定义的对象,需要监视这个目标的 value 值才能触发 watch 的回调函数,相当于监视了一个 reactive ,如果不写 .value 开启deep:true 也可以触发监视回调

wathcEffect函数

watch 是指定监视目标后执行指定的回调。
watchEffect 是不指定监视目标,watchEffect 的回调执行过程中用到哪个目标,就监视哪个目标,并把回调重新执行一次。
注意 watchEffect 相当于开启了 immediate:true 回调会先执行一次。

watchEffect(()=>{
    let x = a.value
    console.log('watchEffect回调执行了')
})

watchEffect 有点像 computed,都可以检测到依赖值的变化,但computed注重返回值,watchEffect 则只执行回调

vue3 生命周期钩子

vue3 除了提供 optionAPI 的生命周期钩子以外,还提供了 compositionAPI 的生命周期钩子。
详情见官方文档

自定义 hook

自定义一个模块,模块里面可以使用各种 vue 的 api,导出之后方便其他vue组件复用。比vue2的mixin好在模块里面可以使用各种vue的api了,例如响应式数据,生命周期钩子,此时 组合式api 的好处就显示出来了。

import { onMounted, onUnmounted, reactive } from "vue";
export default function () {
    const axis = reactive({
        x: 0,
        y: 0
    })
    const clickAxis = (event) => {
        axis.x = event.x;
        axis.y = event.y
    }
    onMounted(
        () => {
            window.addEventListener('click', clickAxis)
        }
    )
    return axis
}

其他组件中导入并使用就可以了

<template>
    <h3>{{ axis }}</h3>
</template>
<script>
import recordClick from '@/hook/recordClick';
export default {
    setup() {
        let axis = recordClick();
        console.log(axis);
        return { axis }
    }
}
</script>

toRef 与toRefs

作用:创建一个 ref 对象,其 value 值指向另一个对象中的某个属性。
参数一为要指向的对象,参数二为要指向对象的属性

const person = reactive({
    name: 'ming',
    age: 18,
    hobby: ['电影','音乐']
})

return {
    name: toRef(person,'name'),
    age: toRef(person,'age')
}

如果不使用 toRef ,则在模板中使用这些数据需要 person.name,person.age这样使用,用了 toRef 之后,就可以单独把 reactive 里面的单条数据包装为一个 ref 数据输出给模板使用。

<template>
    <p>{{name}}</p>
</template>

toRefs 则是把 reactive 整个对象的每一条数据都包装成 ref 输出出去,然后再用展开运算符展开return给模板使用。

const person = reactive({
    name: 'ming',
    age: 18,
    hobby: ['电影','音乐']
})

return {
    ...toRefs(person)
}

如果直接展开person,那么数据不是响应式的,用 toRefs 则是响应式的

shallowReactive 与 shallowRef

shallowReactive:只处理对象最外层的属性为响应式。
shallowRef:只处理自身为响应式,如果传入一个复杂类型的则改复杂类型内部的数据不改为响应式。

let person = shallowReactive({
    name: 'ming',
    age: 18,
    hobby: ['电影','音乐']
})
//此时 hobby 里面的每一项改变都不会触发页面更新

shallowRef 官方解释:
创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的。

const foo = shallowRef({})
// 改变 ref 的值是响应式的
foo.value = {}
// 但是这个值不会被转换。isReactive(foo.value) // false

readonly 与 shallowReadonly

readonly:让一个响应式数据变为只读的(深只读)
shallowReadonly:让一个响应式数据变为浅只读

let person = reactive({
    name: 'ming',
    hobby: ['kk','vv']
})
person = shallowReadonly(person)

toRaw 与 markRaw

toRaw:
作用:将一个 reacttive 响应式对象传入转换为普通对象后return,官方文档说不建议保留对原始对象的持久引用。
markRaw:
作用:标记一个对象,使其永远不会再成为响应式对象。

customRef

创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。
官网示例:使用自定义 ref 通过 v-model 实现 debounce 的示例:

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')
    }
  }}

provide 与 inject 祖后通信

provide接收两个参数,第一个参数是为这个通信的东西起个名字,第二个参数是需要通信的值。
祖先组件中

export default {
  components: {
    HelloWorld,
  },
  setup() {
    const person = reactive({
      name: "ming",
      age: 18
    })
    provide('ming', person)
  }
}

后代组件中

export default {
    setup() {
        let qiming = inject('ming')
        console.log(qiming);
        return { qiming }
    }
}

响应式数据的判断API

  • isRef:检查一个值是否为一个ref对象
  • isReactive:检查是否由 reactive 创建的响应式代理
  • isReadonly:检查是否只读
  • isProxy:

Fragment 组件

现在的 vue3 不需要根标签了,使用 Fragment 实现了虚拟根标签,在开发者工具可以看到

Teleport 组件

teleport 组件可以实现将DOM节点转移至任何 vue 应用上的节点,在to属性指定即可,例如全局遮罩组件转移至根组件

<teleport to="body">
</teleport>

suspense 组件

异步展示组件时会用到,提供两个插槽,默认插槽是真正要展示的内容,替补插槽是如果默认插槽内容没加载完就展示的内容。
可见文档案例

<template>
  <suspense>
    <template #default>
      <todo-list />
    </template>
    <template #fallback>
      <div>
        Loading...
      </div>
    </template>
  </suspense></template>

<script>export default {
  components: {
    TodoList: defineAsyncComponent(() => import('./TodoList.vue'))
  }}</script>

Vue3 其他更改

vue3 全局 API

const app = createApp(App)
app.mount('#app')
2.x全局API 3.x实例API
Vue.config.xxx app.config.xxx
Vue.config.productionTip vue3脚手架自动判断环境
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties

vue3 动画类名

vue2.x写法

.v-enter,
.v-leave-to{
    opacity:0;
}
.v-leave,
.v-enter-to{
    opacity:1;
}

vue3.x写法

.v-enter-from,
.v-leave-to{
    opacity:0;
}
.v-leave-from,
.v-enter-to{
    opacity:1;
}

移除 keyCode 作为 v-on 的修饰符

移除 v-on.native 修饰符

给组件绑定原生事件不用再加 native,如果想把原生事件改为自定义事件,在子组件中使用 emits 配置项声明
父组件:

<my-component
    v-on:click = "handleNativeClickEvent"
/>

子组件:

export default {
    emits:['click']
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容