Vue3 文档学习笔记

vue3学习笔记

setup()

生命周期的与vue2的不同点在:beforeCreatedcreated都在setup()里进行默认调用,其他的都要写在setup()里面,算做compositionAPI

props由父组件传过来的值会自动被响应式,不用再去reactive包一层(注意不能在子组件里直接修改props的值)。

setup()的第一个参数是props(不可进行解构,否则会丢失双向绑定),第二个参数是context,有3个值 attrs,slots,emit,(可进行解构)

props: {
    name: String,
    num: Number
},
setup (props, ctx) {
    props.name // 获取props的name的值
    ctx.emit('add', value) // 在子组件中使用emit像父组件传递值,与vue2中的this.$emit()有较大区别
}

watch()监听两个回调函数,第一个函数return你要监听数据,第二个函数有参数new Value,然后执行你的操作。

reactive, ref

return出去的ref数据在template里不用.value去调用。

如果将一个对象通过ref创建,那么会通过reactive进行创建。

const person = ref({
	name: '李四',
	info: {
		age: 11,
		height: 174
	}
})
console.log(person.value) // 会打印一个Proxy对象,说明是通过reactive创建的

如果在reactive里访问/修改ref,它会自动展开ref,也就是自动给你.value

const count = ref(0)

const state = reactive({
 count
})

console.log(state.count)

如果在reactive里放的是Array或者原生集合(比如Map),他们取值都必须加上.value,不会自动展开

const arr = reactive([ref(100),2])

console.log(arr[0].value)

unref

如果是ref对象就返回通过reactive创建的数据,如果是普通对象,就直接返回

const info = {
	name: '李四',
	age: 22
}
const info2 = ref({
	name: '李四',
	age: 22
})
console.log(unref(info)) // {name: '李四', age: '22'}
console.log(unref(info2)) // Proxy{name: '李四', age: '22'}

// unref就等同于 isRef(info) ? info.value : info

toRef

可以通过toRefreactive响应源数据(source property)里的值单独拿出来使用。转引用一下,用的对方不多。官方示例是对自己写的composition API进行传值。

const toRef1 = reactive({
    name: 'xx',
    age: 11
})
const nameRef = toRef(toRef1, 'name')
nameRef.value = '徐忠炜'
console.log(toRef1.name) // 徐忠炜

toRefs

获取源数据所有数据。在template里直接写属性名就可以使用

<template>
	<h1> {{count}} </h1>
</template>
export default {
  name: 'App',
  setup(props, ctx) {

	const state = reactive({
      count: 4
    })

	return {
		...toRefs(state)
	}
  }
}

customRef

用于防抖节流(图片懒加载、边输入边请求接口的场景)。

computed (API)

跟vue2一样的用法

// 这里用的get
const hello = ref('先生,欢迎来到红浪漫会所。')
const xiaogege = computed(() => {
    return `徐忠炜${hello.value}`
})
console.log(xiaogege.value) // 徐忠炜先生,欢迎来到红浪漫会所。

readonly

让响应式数据或者普通对象都只读,并且是深层的(DEEP)

const read = reactive({
    a:1,
    b: {
        c: 3,
        d: [3,4,5,6]
    }
})
const readOnly = readonly(read)
console.log(readOnly.a) // 1
readOnly.a = 3 // Set operation on key "a" failed: target is readonly.

watchEffect

watchEffect会立即执行(在onBeforeMount之前),并响应式的追踪当前watchEffect里的依赖(数据),并且只要数据改变了它就重新执行一次。当watchEffect写在setup()生命周期里时,在该组件被销毁时它会自动停止监听。

watchEffect还返回一个stop,用于我们手动停止监听。

const stop = watchEffect(() => {
	// do something
})

// later
stop()

Side Effect Invalidation(副作用失效时)

watchEffect即将重新执行/stop时会执行一次onInvalidate方法,watchEffect接收一个onInvalidate参数,其实onInvalidate是一个函数,这个函数的执行顺序是先与watchEffect里面其他所有的程序。

以后慢慢消化SideEffect

const count = ref(0)

count.value = 1

const stop = watchEffect((onInvalidate) => {
	console.log(count.value)
    
    onInvalidate(() => {
        console.log('onInvalidate is triggered')
    })
})
// 打印结果
// 0
// onInvalidate is triggered
// 1

2020-12-03 听至 21p https://www.bilibili.com/video/BV1Q54y1k7At?p=21

watchEffect刷新时间是在所有组件刷新之前调用。也就是执行顺序基本在最前面。

watchEffect的第二个对象(OPTIONS),默认是pre,还有postsync。具体解释看下面

watchEffect(() => {
	console.log(count.value)
}, {
	flush: 'pre' // 呼应上面的话,pre是默认在所有组件刷新之前调用
	flush: 'post' // post是默认在所有组件刷新之后调用,第一次在onBeforeMount之后调用(onMounted之前),之后改变在onbeforeUpdate之后调用(onUpdate之前)
    flush: 'sync'  // 同步执行
})

watchEffect第一次执行时是在mounted之前,如果你要操作DOM或者Template refs请把watchEffect放在onMounted生命周期里

<template>
	<h1 ref='myRef'></h1>
</template>
export default {
  name: 'App',
  setup(props, ctx) {

	const myRef = ref(null)

	watchEffect(() => {
		console.log(myRef.value)
	})

	return {
		myRef
	}
  }
}
//打印结果
// null
// <h1></h1> (dom)

// 如果放在onMounted里面
onMounted(() => {
	watchEffect(() => {
		console.log(myRef.value)
	})
})
// 打印结果
// <h1></h1> (dom)

watch

watch必须监听一个数据,还得有一个回调函数去处理数据

watch(() => {
    return count.value
}, (newValue, oldValue) => {
    // 源数据改变才执行(懒执行)
})

watch监听单一数据有两种写法

// watching a getter
const state = reactive({count: 0})
watch(
	() => state.count,
    (newValue, oldValue) => {
        // do something!
    }
)

// watch a ref
const count = ref(0)
watch(count, (newValue,oldValue) => {
    // do something!
})

watch监听多个源数据

const foo = ref(1)
const bar = ref(2)

setTimeout(() => {
    foo.value = 11
    bar.value = 22
}, 2000)

// 监听要加上.value
watch(() => [foo.value, bar.value], ([newFoo, newBar], [oldFoo, oldBar]) => {
    // do something!
})

watch的执行顺序和watchEffect一样,配置参数也一样,详情往前翻。

isProxy

只有reactivereadonly创建的对象才能被isProxy验证为true

即使new Proxy创建的也为false*

isReactive

一个readonly对象用isReactive验证为false

const plain = readonly({
    name: 'Mary'
})
console.log(isReactive(plain)) // false

如果将一个reactive对象用readonly再包一下,那么验证为true

const state = reactive({
    name: 'John'
})
const stateCopy = readonly(state)
console.log(isReactive(stateCopy)) // true

shallowReactive

很简单,就是浅的响应式,说明嵌套的对象不会被代理,也就是没有响应式

markRaw

以后记得多看,现在用不到

生命周期

生命周期函数只能用在setup()里,并且是同步的。

Options API LifeCycleComposition API LifeCycle 的映射关系

Options API LifeCycleComposition API LifeCycle
beforeCreatesetup()
createdsetup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updateonUpdate
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered

setup()代替了beforeCreatecreate钩子,之前要写在他们里的东西现在只需写在setup()里面。

建议在onMounted里请求数据。

组件卸载时可在onbeforeUnmountonUnmounted里处理一些事件。

onErrorCaptured里捕获子孙组件的异常错误,也就是可以在父组件看到子组件的错误。

onRenderTracked函数里写debugger即可进入调试模式(组件第一次render时)。

onRenderTriggered函数里写debugger即可进入调试模式(rerender时)。

provide/inject (option API)

通常,父子组件传值我们用props。但想象一种情况,当有嵌套比较深的子组件时,比如是个孙组件,那么我们传值必须通过父 -> 子 -> 孙,这样非常麻烦。

为了解决这个情况,我们可以使用provideinject

举个例子:

Root
	TodoList
		TodoItem
		TodoListFooter
			ClearTodoButton
			TodoListStatistics

如果我们想把数据从TodoList传到TodoListStatistics,需经过TodoList->TodoListFooter->TodoListStatistics,但使用provide/inject,我们可以这样做:

// 祖先组件
export default {
    name: 'TodoList',
    provide: {
        name: '徐忠炜'
    }
}

// 孙组件
<template>
    {{ name }}
</template>
export default {
    name: 'TodoListStatistics',
    inject: ['name']
}

!如果我们尝试传递一些组件的实例(绑定在this上),那么它是不会工作的。

如果实在要传实例,那么可以这样写(其实也更推荐这样写,就跟Vue2的data一样)

provide() {
    return {
        name: this.name
    }
}

之前这么做,它的数据是不会有响应式的。因为绑定的provide/inject默认是不被响应的。在Vue2中我们把数据用computed包起来就可以让它变成响应式

provide() {
    return {
        name: Vue.computed(() => '张三')
    }
}

provide/inject (composition API)

普通用法与optionAPI的一样

在setup()里使用provide

provide允许你定义两个参数,第一个是属性名(<string>),第二个是属性值

setup() {
    // 可定义多个
    provide('name', '张三')
    provide('age', 22)
    provide('lover', {
        name: '陈诗诗',
        xiongwei: '70D'
    })
}

在setup()里使用inject

inject允许你填两个参数,第一个是属性名,第二个是默认值(可选)。使用值时要加.value

setup() {
    const name = inject('name')
    const age = inject('age', 18)
    const lover = inject('lover')
    
    return {
        name,
        age,
        lover
    }
}

自带响应式,不用像Vue2里面那样去解决响应式的问题

如果要在inject的那个子组件去改变传递的数据,最好的解决方式是在父组件provide一个方法,方法里面去改变那个值

setup() {
    const lover = ref('nobody')
    const updateLover = () => {
        lover.value = null
    }
    provide('updateLover', updateLover)
}
// 子组件还是一样的方法去获取。完美!

还有,如果你为了杜绝在子组件不小心修改了传递的数据,那么你provide数据的时候给数据加个readonly

provide('name',readonly(name))

2020-12-09学习到 refs https://www.bilibili.com/video/BV1Q54y1k7At?p=36

refs (composition API)

setup()里声明一个refs,然后导出,在视图里进行绑定。

// Child.vue
<template>
    <h1> {{ name }} </h1>
	<button @click='changeName'>changeName</button>
</template>
setup() {
    const child = ref(null) 
    const name = ref('张三')
    
    const changeName = () => {
        console.log(child.value) // 打印的dom
        child.value.innerText = '李四'
    }
    
    return {
        child,
        changeName
    }
}

// 如果想在父组件调用子组件的方法或数据
// Father.vue
<template>
    <child ref='child'></child>
</template>
setup() {
    const child = ref(null)
    
    console.log(child.value.name) // 张三
}

遍历生成refs

<div v-for='(item, i) in list' :ref='el => { if (el) divs[i] = el }'></div>

setup() {
    const divs = ref([])
    
    onMounted(() => {
        console.log(divs.value) // 打印出经过代理的dom节点
    })
}

Application Config

Application Config

const app = Vue.createApp({})
app.config = {
    ...
}

errorHandler

// 基本用不到
app.config.errorHandler = (err, vm, info) => {
    // handle error
    // 'info' 是Vue的特别的错误信息,比如生命周期呀。
}

warnHandler

// 基本用不到
app.config.warnHandler  = (msg, vm, trace) => {
    // 在运行时产生的一些警告
    // 'trace' 
}

global properties

添加一个全局属性,在任何组件实例里都能引用

app.config.globalProperties.foo = 'bar'

app.component('child-component', {
    mounted() {
        console.log(this.foo) // 'bar'
    }
})

就相当于在Vue2.x中,我们往Vue实例上绑东西是这样的

Vue.prototype.$axios = axios

在Vue3中,应该这样做

const app = Vue.createApp({})
app.config.globalProperties.$axios = axios

在组件中调用全局属性

// main.js
app.config.globalProperties.token = 'aofhbioasdlfbnislfbsdkl'

// App.vue
// 获取globalProperties
const instace = getCurrentInstance()
console.log(`全局的token:${instace.ctx.token}`)

isCustomElement

使用方法,不报错的。

// vue.config.js
module.exports = {
    vueCompilerOptions: {
        isCustomElement: tag => /^jsjiajia-/.test(tag)
    }
}

Application API

const app = Vue.createApp({})

component 全局注册组件

app.component('componentName', component)

directive 自定义指令

实例一:不使用自定义指令,实现tab栏切换

<template>
    <div class='tab' @click='handleTabClick($event)'>
    	<button data-index='0' :class='['tab-btn', {'active': curIndex === 0}]'></button>
		<button data-index='1' :class='['tab-btn', {'active': curIndex === 1}]'></button>
		<button data-index='2' :class='['tab-btn', {'active': curIndex === 2}]'></button>
    </div>
</template>
export default {
    setup() {
        const curIndex = ref(0)
        
        const handleTabClick = e => {
            const tar = e.target
            const className = tar.className
            const index = parseInt(tar.dataset.index)
            
            if (className === 'tab-btn') {
                curIndex.value = index
            }
        }
        
        return {
            curIndex,
            handleTabClick
        }
    }
}

实例二:使用自定义指令

<template>
    <div class='tab' @click='handleTabClick($event)' v-tab="{ className: 'tab-btn', activeClass: 'active', curIndex}">
    	<button data-index='0' class='tab-btn'></button>
		<button data-index='1' class='tab-btn'></button>
		<button data-index='2' class='tab-btn'></button>
    </div>
</template>
<script>
	import tab from './tab.js'
    export default {
        directive: {
            tab
        },
        setup() {
            const curIndex = ref(0)

            const handleTabClick = e => {
                const tar = e.target
                const className = tar.className
                const index = parseInt(tar.dataset.index)

                if (className === 'tab-btn') {
                    curIndex.value = index
                }
            }

            return {
                curIndex,
                handleTabClick
            }
        }
    }
</script>

// tab.js
export default {
    mounted(el,binding) {
        // el是指令挂载的对象
        // binding对象的value包含了传过来的值 binding.value.activeClass
        const { className, activeClass, curIndex } = binding.value
        const btns = el.getElementsByClassName(className)
        btns[curIndex].className += ` ${activeClass}`
    },
    update(el,binding) {
        const { className, activeClass, curIndex } = binding.value
        const { oldIndex } = binding.oldValue
        
        const btns = el.getElementsByClassName(className)
        btns[oldIndex].className = className
        btns[curIndex].className += ` ${activeClass}`
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值