- 一个组件选项,在组件被创建之前,
props
被解析之后执行。 - 它是组合式 API 的入口。
const app = Vue.createApp({
template: `
<div @click=handleClick>{{message}}</div>
`,
setup(props, context) {
// return 中返回的属性可以在template中使用
return {
message: 'hello',
handleClick: () => {
console.log(666);
}
}
}
});

二、ref、reactive响应式引用的用法和原理
- 原理:通过 proxy 对数据进行封装,当数据变化时,触发模板等内容的更新。
1. ref 的使用
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
// 引入 ref
const { ref } = Vue;
// 'hy' 变成 proxy({value: 'hy'}) 这样的一个响应式引用
let name = ref('hy');
setTimeout(() => {
name.value = 'hy666'
}, 2000);
return { name };
}
});

const app = Vue.createApp({
template: `
<div>{{nameObj}}</div>
`,
setup(props, context) {
// 引入 reactive
const { reactive } = Vue;
// {name: 'hy'} 变成 proxy({name: 'hy'}) 这样的一个响应式引用
const nameObj = reactive({name: 'hy'})
setTimeout(() => {
nameObj.name = 'hy666'
}, 2000);
return { nameObj };
}
});

- 接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。
- 只读代理是深层的:任何被访问的嵌套 property 也是只读的,修改时会报警告。
- 注意:使用 vue3.2.23 版本时,没有警告弹出,可以选择其它版本。
const app = Vue.createApp({
template: `
<div>{{myArray}}--{{copyMyArray}}</div>
`,
setup(props, context) {
// 引入 readonly
const { reactive, readonly } = Vue;
const myArray = reactive([123]);
const copyMyArray = readonly(myArray);
setTimeout(() => {
myArray[0] = 666;
copyMyArray[0] = 777;
}, 2000);
return { myArray, copyMyArray };
}
});

- 将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。
- 即: 将 proxy({ name: 'hy' }) 转化为 { name: proxy({ value: 'hy' }) }
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
// 引入 toRefs
const { reactive, toRefs } = Vue;
const nameObj = reactive({ name: 'hy' });
setTimeout(() => {
nameObj.name = 'hy6666';
}, 2000);
const { name } = toRefs(nameObj);
console.log(name);
return { name };
}
});
三、toRef 以及 context 参数
- 可以用来为源响应式对象上的某个 property 新创建一个 ref。
- 即使源 property 不存在,
toRef
也会返回一个可用的 ref。
const app = Vue.createApp({
template: `
<div>{{age}}</div>
`,
setup(props, context) {
// 引入 toRef
const { reactive, toRef } = Vue;
const nameObj = reactive({ name: 'hy' });
// 注意接收时不需要解构
const age = toRef(nameObj, 'age');
setTimeout(() => {
age.value = 28;
console.log(age);
}, 2000);
return { age };
}
});

- 传递给
setup
函数的第二个参数是 context
。 context
是一个普通 JavaScript 对象,暴露了其它可能在 setup
中有用的值:attrs、slots、emit 。- attrs:Attribute (非响应式对象,等同于 $attrs)。
const app = Vue.createApp({
template: `<child app='app'/>`
});
app.component('child', {
setup(props, context) {
const { attrs, slots, emit } = context;
console.log(attrs); // None-props 属性
return { attrs }
},
template: `<div>{{attrs.app}}</div>`
});
- slots:插槽 (非响应式对象,等同于 $slots)。
- $slots:用来以编程方式访问通过插槽分发的内容。每个具名插槽都有其相应的 property (例如:
v-slot:foo
中的内容将会在 this.$slots.foo()
中被找到)。default
property 包括了所有没有被包含在具名插槽中的节点,或 v-slot:default
的内容。
const app = Vue.createApp({
template: `<child>hello</child>`
});
app.component('child', {
setup(props, context) {
const { h } = Vue;
const { attrs, slots, emit } = context;
// slots.default() 返回的是一个虚拟DOM节点
console.log(slots.default());
return () => h('div', {}, slots.default());
},
});
- emit:触发事件 (方法,等同于 $emit)。
const app = Vue.createApp({
methods: {
handleChange() {
console.log('change');
}
},
template: `<child @change='handleChange'/>`
});
app.component('child', {
template: `<div @click='handleClick'>hello</div>`,
setup(props, context) {
const { attrs, slots, emit } = context;
// 点击触发 change 事件
function handleClick() { emit('change'); }
return { handleClick };
},
});

1. 接受一个回调函数
// computed计算属性
const app = Vue.createApp({
setup() {
const { ref, computed } = Vue;
const count = ref(0);
const handleClick = () => {
count.value += 1;
}
const countAddSix = computed(() => {
return count.value + 6;
})
return { count, handleClick, countAddSix };
},
template: `
<div @click='handleClick'>
{{count}}--{{countAddSix}}
</div>
`
});

2. 接受一个具有 get 和 set 函数的对象
// computed计算属性
const app = Vue.createApp({
setup() {
const { ref, computed } = Vue;
const count = ref(0);
const handleClick = () => {
count.value += 1;
};
const countAddSix = computed({
get: () => {
return count.value + 6;
},
set: val => {
count.value = val - 6;
}
});
setTimeout(() => {
countAddSix.value = 100;
}, 1000)
return { count, handleClick, countAddSix };
},
template: `
<div @click='handleClick'>
{{count}}--{{countAddSix}}
</div>
`
});

五、watch 和 watchEffect 的使用和差异性
- 具备一定的惰性(第一次不执行)。
- 参数可以拿到原始值和当前值。
- 可以侦听多个数据的变化,用一个侦听器承载。
const app = Vue.createApp({
setup() {
const { reactive, watch, toRefs } = Vue;
const nameObj = reactive({
name: '小吴',
engName: 'hy'
});
watch(
[() => nameObj.name, () => nameObj.engName],
([curName, curEngName], [preName, preEngName]) => {
console.log(curName, preName, '---', curEngName, preEngName);
}
);
setTimeout(() => {
nameObj.name = '小豪';
nameObj.engName = 'xh';
}, 1000);
const { name, engName } = toRefs(nameObj);
return { name, engName };
},
template: `
<div>
name: {{name}}--engName: {{engName}}
</div>
`
});

- 立即执行,没有惰性。
- 不需要传递侦听的内容,会自动感知代码依赖。
- 不需要传递很多参数,只要传递一个回调函数。
- 不能获取之前数据的值。
const app = Vue.createApp({
setup() {
const { reactive, watch, watchEffect, toRefs } = Vue;
const nameObj = reactive({
name: '小吴',
engName: 'hy'
});
watchEffect(()=>{
console.log(`${nameObj.name}---${nameObj.engName}`);
});
setTimeout(() => {
nameObj.name = '小豪';
nameObj.engName = 'xh';
}, 1000);
const { name, engName } = toRefs(nameObj);
return { name, engName };
},
template: `
<div>
name: {{name}}--engName: {{engName}}
</div>
`
});

- onRenderTracked: 第一次渲染时收集响应式依赖。
- onRenderTriggered: 每次触发页面重新渲染时自动执行。
const app = Vue.createApp({
setup() {
const {
ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onRenderTracked, onRenderTriggered } = Vue;
const name = ref('hy');
onBeforeMount(() => {
console.log('onBeforeMount');
});
onMounted(() => {
console.log('onMounted');
});
onBeforeUpdate(() => {
console.log('onBeforeUpdate');
});
onUpdated(() => {
console.log('onUpdated');
});
onBeforeUnmount(() => {
console.log('onBeforeUnmount');
});
onUnmounted(() => {
console.log('onUnmounted');
});
onRenderTracked(() => {
console.log('onRenderTracked');
});
onRenderTriggered(() => {
console.log('onRenderTriggered');
});
setTimeout(() => {
name.value = 'xh';
}, 1000);
return { name };
},
template: `
<div>
name: {{name}}
</div>
`
});
setTimeout(() => {
app.unmount();
}, 2000);

七、provide、inject 以及模板引用的用法
// provide、inject
const app = Vue.createApp({
setup() {
const { ref, provide, readonly } = Vue;
const name = ref('hy');
// 1.key, 2.value
provide('name', readonly(name));
provide('changeName', (value) => {
name.value = value
});
},
template: `
<child/>
`
});
app.component('child', {
setup() {
const { inject } = Vue;
// 1.key, 2.默认值
const name = inject('name', 'hello');
const changeName = inject('changeName');
const handleClick = () => {
changeName('xh');
};
return { name, handleClick };
},
template: `
<div @click='handleClick'>{{name}}</div>
`
});

// CompositionAPI 的语法下,获取真实的 DOM 元素节点
const app = Vue.createApp({
setup() {
const { ref, onMounted } = Vue;
const hello = ref(null);
onMounted(() => {
console.log(hello.value);
});
return { hello };
},
template: `
<div ref="hello">hello world</div>
`
});
