1、setup函数
vue3为了方便开发,实现数据,函数的统一管理,开放setup函数,setup函数的运行介于beforeCreated与created函数之间运行, 接收两个函数 props, context, 并且该方法返回一个object对象,导出的变量和函数就可以在template中进行使用
const app = Vue.createApp({ template: `<div> <h1>this is test</h1> <button @click='clickEvent'>按钮</button> </div>`, setup(props, context) { const clickEvent = () => { console.log(props, context) } return { clickEvent } } }) const vm = app.mount('#root')
注意: 因为setup是在created之前,即实例创建之前运行的,所以无法调用this这个对象,也没有办法调用methods里的方法, data里的变量
setup接收两个参数,props与context, props是外部传入到组件中的值 ,注意:不要对props进行解构,这样会失去响应,context中可以得到attrs, 这个attrs区别于props在于,attrs表示该组件未接收的参数,而props表示被接收的参数,emit表示发送机制,slot表示插槽对象
const {getCurrentInstance} = Vue const myPlugin = (app) => { app.config.globalProperties.$say = () => { console.log('ok') } } const app = Vue.createApp({ template: `<div> <h1>this is test</h1> <button @click='clickEvent'>change</button> </div>`, setup(props, {attrs, emit, slots}) { const { ctx } = getCurrentInstance() const clickEvent = () => { console.log(ctx) console.log(attrs, emit, slots) } return { clickEvent } } }) const vm = app.mount('#root')
注意:如果需要调用全局vue实例下的方法或值,如vue.config.globalProperties下的方法,需要用到getCurrentInstance来获取
2、ref与reactive, readonly的使用
在setup函数中,因为得不到this所以没有办法实现data数据响应,为此实现了另外一套响应式方式, ref对String, Number, Boolean类型进行响应,而reactive是实现对object以及array实现响应
const {ref, reactive, readonly} = Vue const app = Vue.createApp({ template: `<div> <h1>this is test</h1> <span>name---{{nick}}</span> <button @click='changeName(), changeInfo(), changeJson()'>change</button> </div>`, setup(props, context) { const name = ref('bill') const changeName = () => { name.value = 'jeere' } const info = reactive({ title: 'this is title', subTitle: 'this is subTitle' }) const changeInfo = () => { info.title = 'this is changeTitle' } //如果用readonly来修饰ref或者reactive,那么数据相当于冻结,不可改变 //如修饰ref readonly(ref('bill')) //如修饰reactive reaadonly(reactive({ // title: 'this is title', // subTitle: 'this is subTitle' // }) //也可以用来冻结普通对象 const json = readonly({fav: 'computed'}) const changeJson = () => { json.fav='game' console.log(json) } return { nick: name, changeName, info, changeInfo, changeJson } } }) const vm = app.mount('#root')
在可以利用react的useState对ref进行二次封装
const useSimgle = (value) => { const val = Vue.ref(value); setVal = (newVal) => { val.value = newVal } return [val, setVal] } const app = Vue.createApp({ template: `<div> <h1>this is test</h1> <span>title---{{title}}, name --- {{name}}</span> <button @click='clickEvent'>change</button> </div>`, setup(props, {attrs, emit, slots}) { const [title, setTitle] = useSimgle('this is test') const [name, setName] = useSimgle('bill') const clickEvent = () => { setTitle('this is ok'); // setName('jeere') } return { title, name, clickEvent } } }) const vm = app.mount('#root')
3、toRef与toRefs的使用
如果对reactive后的数据进行解构出来使用,这时为了实现解构出来的数据的响应,就需要使用toRefs进行修饰,如果原本reactive里的数据没有,那么这时需要对原来没有的字段进行添加,就需要使用到toRef。
const {reactive, toRefs, toRef} = Vue const app = Vue.createApp({ template: `<div> <h1>this is test</h1> <span>name---{{name}}</span> <span>age---{{age}}</span> <button @click='changeName(), changeAge()'>change</button> </div>`, setup() { const info = reactive({ name: 'bill' }); const changeName = () => { info.name = 'jeere' } // 进行解构 const {name} = toRefs(info); // 会对info对象进行递归遍历,把所有项的值都使用proxy进行代理监听 // 因为原本age是没有值的,所以无需要赋值为undefined,所有无需要解构,返回的是一个ref对象 const age = toRef(info, 'age') // 会对info中原本没有的值,进行赋值为undefined const changeAge = () => { info.age = 32 } return { name, changeName, age, changeAge } } }) const vm = app.mount('#root')
注意:通常来讲,toRefs用的相对会多,但是toRef用的相对会少些,因为,这样不便于数据的管理
4、setup下的计算属性
在setup函数下使用computed属性,那么就需要借助vue里的computed函数,这个函数接收两种参数,一种是函数,一种是一个object对象---可以使用get与set方法实现获取与设置
const useSimgle = (value) => { const val = Vue.ref(value); setVal = (newVal) => { val.value = newVal } return [val, setVal] } const { computed } = Vue const app = Vue.createApp({ template: `<div> <h1>this is test</h1> <span>当前数量---{{showCount}}</span> <button @click='clickEvent'>change</button> </div>`, setup(props, {attrs, emit, slots}) { const [count, setCount] = useSimgle(0) // const showCount = computed(() => { // return count.value + '个' // }) // const clickEvent = () => { // setCount(count.value+1) // } const showCount = computed({ get() { return count.value + '个' }, set(val) { setCount(val) } }) const clickEvent = () => { showCount.value = '123' } return { showCount, clickEvent } } }) const vm = app.mount('#root')
注意:红色部份是使用传入函数的方式,而蓝色部份是使用传入Object对象的方式
5、setup下的watch以及watchEffect
watch监听非对象,非数组数据
const useSimgle = (value) => { const val = Vue.ref(value); setVal = (newVal) => { val.value = newVal } return [val, setVal] } const { watch, watchEffect } = Vue const app = Vue.createApp({ template: `<div> <div><span>name---{{name}}</span> | <span>age---{{age}}</span></div> <button @click='clickEvent'>change</button> </div>`, setup(props, {attrs, emit, slots}) { const [name, setName] = useSimgle('bill'); const [age, setAge] = useSimgle(30); //单个数据的监听 // watch(name, (curr, prev) => { // console.log(curr, prev) // }) // watch(age, (curr, prev) => { // console.log(curr, prev) // }) //多个数据的监听 watch([name, age], (a, b) => { console.log(a, b) }) const clickEvent = () => { setName('jeere'); setAge(20); } return { name, age, clickEvent } } }) const vm = app.mount('#root')
监听reactive对象
const { watch, watchEffect, toRefs, reactive } = Vue const app = Vue.createApp({ template: `<div> <div><span>name---{{name}}</span> | <span>age---{{age}}</span></div> <button @click='clickEvent'>change</button> </div>`, setup(props, {attrs, emit, slots}) { const info = reactive({ name: 'bill', age: 30 }) //监听单个变量 // watch(() => info.name, (current, prev) => { // console.log(current, prev) // }) // watch(() => info.age, (current, prev) => { // console.log(current, prev) // }) //监听多个变量 watch([() => info.name, () => info.age], (current, prev) => { console.log(current, prev) }) const {name, age} = toRefs(info); const clickEvent = () => { info.name = 'jeere'; info.age = 20; } return { name, age, clickEvent } }
watch可以配置配置项
const useSimgle = (value) => { const val = Vue.ref(value); setVal = (newVal) => { val.value = newVal } return [val, setVal] } const { watch, watchEffect, toRefs, reactive } = Vue const app = Vue.createApp({ template: `<div> <button @click='clickEvent'>change</button> </div>`, setup(props, {attrs, emit, slots}) { let info = reactive({ name: 'bill', age: 30, props: { fav: ['computed'] } }) watch(info, (cur, prev) => { console.log(cur, prev) }, { immediate: true //这个是配置是立即执行 }) const clickEvent = () => { info.props.fav[0] = 'sport' } return { clickEvent } } }) const vm = app.mount('#root')
watchEffect的使用
const { watch, watchEffect, toRefs, reactive } = Vue const app = Vue.createApp({ template: `<div> <button @click='clickEvent'>change</button> </div>`, setup(props, {attrs, emit, slots}) { let info = reactive({ name: 'bill', age: 30, list: ['aaa'] }) watchEffect(() => { console.log(info.name) console.log(info.age) }) const clickEvent = () => { info.name='jeere' info.age=20 } return { clickEvent } } }) const vm = app.mount('#root')
注意:watchEffect与watch的区别在于,watchEffect是setup后立即执行,里面接收一个函数参数以及后面的参数为配置项,该方法会感知所调用的变量,实现自动感知刷新
6、setup函数下的生命钩子函数
以下是生命钩子函数的映射表
beforeCreate -> use setup() created -> use setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeUnmount -> onBeforeUnmount unmounted -> onUnmounted errorCaptured -> onErrorCaptured renderTracked -> onRenderTracked renderTriggered -> onRenderTriggered activated -> onActivated deactivated -> onDeactivated
7、setup中使用provide与inject
在setup函数中使用provide(key, value)表示向子组件或孙组件提供值,子组件接收用const key = inject('key', default value)进行接收
如果provide向下传的是一个ref对象,那么子组件也可以修改对应的值 ,这时为了避免子组件修改,可以使用readonly进行修饰
const useSimgle = (value) => { const val = Vue.ref(value); setVal = (newVal) => { val.value = newVal } return [val, setVal] } const { provide, inject, onBeforeMount, readonly } = Vue const Item = { template: `<div>{{title}} <button @click='clickEvent'>change</button></div>`, setup() { const title = inject('title', 'default') const setTitle = inject('setTitle') onBeforeMount(() => { console.log(title) }) const clickEvent = () => { title.value = 'nono' } return { title, clickEvent } } } const app = Vue.createApp({ template: `<div> <Item /> </div>`, components: { Item }, setup(props, {attrs, emit, slots}) { const [title, setTitle] = useSimgle('title'); provide('title', readonly(title)) provide('setTitle', setTitle) } }) const vm = app.mount('#root')
注意:使用以上方法需要注意一个原则就是vue是单向数据流
8、setup函数中使用dom的ref获取节点
在setup中需要获取dom节点也是使用ref但是方法有所不同,具体如下
const { ref } = Vue const app = Vue.createApp({ template: `<div> <h2 ref='title'>this is title</h2> <button @click='clickEvent'>click</button> </div>`, setup(props, {attrs, emit, slots}) { const title = ref(null); const clickEvent = () => { console.log(title) } return { title, clickEvent } } }) const vm = app.mount('#root')
注意:首先定义一个ref并且赋值为null,然后并且把这个ref对象从setup中return出来,并且在dom上设置ref属性,名称与setup上一致,这样就可以获取这个节点了
9、setup与typescript相结合后,props的类型声明
<script lang="ts"> import { defineComponent, PropType, ref } from 'vue' const PropsType = { msg: { type: String as PropType<string> // 作typescript的类型匹配 }, title: { type: Number as PropType<number>, // 可以是普通类型,也可以是一个接口类型的数据 required: true } } as const // 为了能够精准的匹配,后面需要跟一个as const export default defineComponent({ name: 'HelloWorld', props: PropsType, setup (props) { const name = ref<string>('bill') return { name } } }) </script>
注意:经过上面配置后就可以在setup中使用props,并且有提示
10、在vue3中配置使用tsx语法
npm install @vue/babel-plugin-jsx -D
安装对应依赖后,需要对babel.config.js进行配置
{ "plugins": ["@vue/babel-plugin-jsx"] }
这样就可以愉快的进行tsx的编写了,可以把组件定义为tsx为后缀内容如下
import { defineComponent } from 'vue'
export default defineComponent({
name: 'HelloWorld',
setup () {
return () => {
return <div>this is test</div>
}
}
})
注意:使用tsx进行组件的编写,配合的样式编写应为vue3-styled-components npm i vue3-styled-components -D进行安装 ,相应的用法参考网上