1. provide传多个函数/属性
常规
js
复制代码
// parent.vue
const key = Symbol('sonContext')
provide(key,{
fn1:()=>{},
fn2:()=>{}
})
// son.vue
const context = inject(key)
context?.fn1()
进阶
js
复制代码
// 封装成hook,调用方只关注传值即可,key在hook维护与组件解耦
import { inject, provide } from 'vue'
function useCreateProvide(key){
// key也可以不传写死,每次调用都是最新的Symbol
// const key = Symbol('provideKey')
return [
function useProvide(value){
provide(key,value)
},
function useInject(defaultValue){
return inject(key,defaultValue)
}
]
}
const [useUserProvide,useUserInject] = createProvideState(Symbol('user'))
const [useSonProvide,useSonInject] = createProvideState(Symbol('son'))
export { useUserProvide, useUserInject, useSonProvide, useSonInject }
2.异步组件
js
复制代码
// 场景 组件从远程js获取 需要二次处理
import { h } from 'vue'
const items = [
{type:'input',key:'name'},
{url:'https://cdn.jsdelivr.net/npm/element-plus@2.10.4/es/components/affix/index.mjs'}
]
const Comp = {
setup(_, { attrs, slots }) {
const component = () =>
defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
render() {
return h('div', 'hello world')
},
})
}, 1000)
})
})
return h(component, attrs, slots)
},
}
js
复制代码
const Comp = {
props:['item'],
setup(props){
const component = ref(()=>return h('div',''))
const url = props.item.url || 'https://unpkg.com/ant-design-vue/lib/index.js'
const name = props.item.name || 'AButton'
function loadScript(){
import(url).then(module=>{
const Comp = module[name]
component.value = Comp
})
}
loadScript()
return h(component.value)
}
}
- 有了上面的基础来实现defineAsyncComponent
js
复制代码
//loader是个函数返回promise resove一个组件
function defineAsyncCompont(loader) {
return {
setup() {
const componet = ref(() => h('span'))
loader().then((res) => {
// 处理一下 defineAsyncCompont(import('./Button.js'))
if (res && res[Symbol.toStringTag] === 'module') {
res = res.default
}
componet.value = res
})
return () => {
return h(componet.value)
}
},
}
}
- 进阶(defineAsyncComponent接收对象可定义loader和loading error组件)
js
复制代码
import { h } from 'vue'
const Comp = {
setup(_, { attrs, slots }) {
const component = () =>
defineAsyncComponent({
loader: () => import('./ceshi.vue'),
loadingComponent: {
render(){
return h('div','loading')
}
},
errorComponent: () => import('./error.vue'),
})
return h(component, attrs, slots)
},
}
js
复制代码
export function defineAsyncComponent(options) {
if (typeof options === 'function') {
options = {
loader: options,
}
}
// 占位符组件
const defaultComponent = () => h('span')
const {
loader,
loadingComponent = defaultComponent,
errorComponent = defaultComponent,
timeout,
} = options
return {
setup(_, { attrs, slots }) {
const component = shallowRef(loadingComponent)
function loadComponent() {
return new Promise((resolve, reject) => {
/**
* 到点了,还没完成,我就调用 reject
*/
if (timeout && timeout > 0) {
setTimeout(() => {
reject('超时了')
}, timeout)
}
loader().then(resolve, reject)
})
}
/**
* loader 函数返回一个 Promise
* 但是这个 Promise 我们不能控制它的成功和拒绝
*/
loadComponent()
.then((res) => {
if (res && res[Symbol.toStringTag] === 'Module') {
res = res.default
}
/**
* 1000 ms 会进来
*/
component.value = res
})
.catch(() => {
// 组件加载失败了,改为 defaultComponent
component.value = errorComponent
})
return () => {
return h('div', [h(component.value, attrs, slots)])
}
},
}
}