一、Vue 基础之 setup、ref 及 reactive、toRef 及 context、TodoList、computed、watch 及 watchEffect、生命周期函数新写法、provide 及 inject
setup
是在 created
实例被完全初始化之前执行的函数,接收 props
和 context
参数。在 setup
中返回出来的数据和方法可以被模版、外部等使用,但不能使用外部的数据和方法,因为此时 app
实例并没有被挂载,同时 setup
中是不能使用 this
以及实例上的方法,如下所示:
const app = Vue.createApp({
template: `
<div @click="handleClick">{{name}}</div>
`,
methods: {
test() {
console.log(this.$options.setup());
}
},
mounted() {
this.test();
},
setup(props, context) {
return {
name: 'Tom',
handleClick: () => {
alert(123)
}
}
}
});
const vm = app.mount('#root');
ref、reactive
响应式的引用,原理是通过 proxy
对数据进行封装,当数据变化时,触发模版等内容的更新。ref
是可以处理基础类型的数据,如 ref('Tom')
,proxy 'Tom'
变成 proxy({value: 'Tom'})
这样的一个响应式引用,而响应式引用在改变值以后,页面会跟着变化的。reactive
处理非基础类型的数据,如 reactive({name: 'Tom'})
,proxy {name: 'Tom'}
变成 proxy({name: 'Tom'})
这样的一个响应式引用。readonly
可以对响应式引用进行限制,通过 readonly
处理返回的对象是不可以被响应式修改的。toRefs
可以将 reactive
中的数据处理成响应式的数据,如 toRefs proxy({name: 'Tom', age: 24})
变成 { name: proxy({value: 'Tom'})
, age: proxy({value: 24})}
,如下所示:
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
const { reactive, readonly, toRefs } = Vue;
const nameObj = reactive({name: 'Tom', age: 24});
setTimeout(() => {
nameObj.name = 'Jack'
}, 2000)
const { name, age } = toRefs(nameObj);
return { nameObj, copyNameObj, name, age }
}
});
const vm = app.mount('#root');
toRef
,当对象里面没有对应属性值的时候,又想要这个属性值具备响应式特性的时候,可以使用 toRef
,如下所示:
const app = Vue.createApp({
template: `
<div>{{age}}</div>
`,
setup(props, context) {
const { reactive, toRef } = Vue;
const data = reactive({name: 'Tom'});
const age = toRef(data, 'age');
setTimeout(() => {
age.value = 24
}, 2000);
return { age }
}
});
const vm = app.mount('#root');
- 在
setup
的 context
参数中,包括 attrs、slots、emit
等,attrs
是父组件传递过来的 none-props
属性,slots
是实现之前 this.$slots
,emit
是实现之前 this.$emit
,如下所示:
const app = Vue.createApp({
methods: {
handleChange() {
alert('change');
}
},
template: `
<child @change="handleChange">parent</child>
`,
});
app.component('child', {
template: `
<div @click="handleClick">123</div>
`,
setup(props, context) {
const { h } = Vue;
const { attrs, slots, emit } = context;
function handleClick() { emit('change'); }
return { handleClick }
}
});
const vm = app.mount('#root');
- 实现
TodoList
,如下所示:
const listRelativeEffect = () => {
const { reactive } = Vue;
const list = reactive([]);
const addItemToList = (item) => {
list.push(item);
}
return { list, addItemToList }
}
const inputRelativeEffect = () => {
const { ref } = Vue;
const inputValue = ref('');
const handleInputValueChange = (e) => {
inputValue.value = e.target.value
}
return { inputValue, handleInputValueChange }
}
const app = Vue.createApp({
setup() {
const { list, addItemToList } = listRelativeEffect();
const { inputValue, handleInputValueChange } = inputRelativeEffect();
return {
list, addItemToList, inputValue, handleInputValueChange
}
},
template: `
<div>
<div>
<input :value="inputValue" @input="handleInputValueChange" />
<button @click="() => addItemToList(inputValue)">提交</button>
</div>
<ul>
<li v-for="(item, index) in list" :key="index">{{item}}</li>
</ul>
</div>
`
});
const vm = app.mount('#root');
computed
计算属性,如下所示:
const app = Vue.createApp({
setup() {
const { ref, computed, reactive } = Vue;
const countObj = reactive({count: 0});
const handleClick = () => {
countObj.count += 1;
}
let countAddFive = computed({
get: () => {
return countObj.count + 5;
},
set: (param) => {
countObj.count = param - 5;
}
})
setTimeout(() => {
countAddFive.value = 100;
}, 3000)
return { count, countObj, countAddFive, handleClick, }
},
template: `
<div>
<span @click="handleClick">{{countObj.count}}</span> -- {{countAddFive}}
</div>
`
});
const vm = app.mount('#root');
watch
侦听器,具备一定的惰性 lazy
,参数可以拿到原始和当前值,可以侦听多个数据的变化,用一个侦听器承载。watchEffect
侦听器,偏向于 effect
,立即执行,没有惰性 immediate
,不需要传递你要侦听的内容,自动会感知代码依赖,不需要传递很多参数,只要传递一个回调函数,不能获取之前数据的值,如下所示:
const app = Vue.createApp({
setup() {
const { reactive, watch, toRefs, watchEffect } = Vue;
const nameObj = reactive({
name: 'Tom', englishName: 'Jack'
});
watch([() => nameObj.name, () => nameObj.englishName], ([curName, curEng], [prevName, preEng]) => {
console.log(curName, prevName, '---', curEng, preEng);
}, { immediate: true });
const stop = watchEffect(() => {
console.log(nameObj.name);
console.log(nameObj.englishName);
setTimeout(() => {
stop();
}, 5000)
})
const { name, englishName } = toRefs(nameObj);
return { name, englishName }
},
template: `
<div>
<div>
Name: <input v-model="name" />
</div>
<div>
Name is {{ name }}
</div>
<div>
EnglishName: <input v-model="englishName" >
</div>
<div>
EnglishName is {{EnglishName}}
</div>
</div>
`
});
const vm = app.mount('#root');
- 生命周期函数,
beforeMount -> onBeforeMount,mounted -> onMounted,beforeUpdate -> onBeforeUpdate,beforeUnmount -> onBeforeUnmount,unmounted -> onUnmounted
,onRenderTracked
是当页面渲染后 Vue
会重新收集响应式的依赖而自动执行,也就是每次渲染后重新收集响应式依赖,onRenderTriggered
是每次触发页面重新渲染时自动执行,如下所示:
const app = Vue.createApp({
setup() {
const { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onRenderTracked, onRenderTriggered } = Vue;
const name = ref('Tom');
onBeforeMount(() => {
console.log('onBeforeMount')
})
onMounted(() => {
console.log('onMounted')
})
onBeforeUpdate(() => {
console.log('onBeforeUpdate')
})
onUpdated(() => {
console.log('onUpdated')
})
onRenderTracked(() => {
console.log('onRenderTracked')
})
onRenderTriggered(() => {
console.log('onRenderTriggered')
})
const handleClick = () => {
name.value = 'Tom'
}
return { name, handleClick }
},
template: `
<div @click="handleClick">{{name}}</div>
`
});
const vm = app.mount('#root');
provide、inject,CompositionAPI
的语法下,ref
获取真实的 DOM
元素节点,如下所示:
const app = Vue.createApp({
setup() {
const { provide, ref, readonly } = Vue;
const name = ref('Tom');
provide('name', readonly(name));
provide('changeName', (value) => {
name.value = value;
});
return { }
},
template: `
<div>
<child />
</div>
`,
});
app.component('child', {
setup() {
const { inject } = Vue;
const name = inject('name');
const changeName = inject('changeName');
const handleClick = () => {
changeName('Jack')
}
return { name, handleClick }
},
template: `
<div @click="handleClick">{{name}}</div>
`
});
const vm = app.mount('#root');