北大青鸟教网站开发吗,网络营销的产品策略,网站如何防止恶意注册,怎么做淘客手机网站一、依赖注入
1、简介
在前面的笔记中#xff0c;我们学习过父组件向子组件传递数据时#xff0c;需要借助props来实现。但如果父组件想要向孙子组件传递数据#xff0c;那就要连续使用两层props逐级向下传递#xff0c;如果要接收数据的是更深层的后代组件#xff0…一、依赖注入
1、简介
在前面的笔记中我们学习过父组件向子组件传递数据时需要借助props来实现。但如果父组件想要向孙子组件传递数据那就要连续使用两层props逐级向下传递如果要接收数据的是更深层的后代组件则需要连续使用多层props进行逐级传递代码十分繁琐。
依赖注入provide/inject就是来解决这一问题的该特性可以实现父组件向其后代组件跨层级传递数据只要父组件向其后代组件提供了依赖provide无论两者中间相隔多少组件层级后代组件都可以注入inject父组件提供的依赖从而实现跨组件层级的数据交互。但过多的使用provide/inject 特性会增加组件之间的耦合性降低组件的可复用性因此不可滥用。
2、provide提供依赖
局部provide
父组件要给后代组件传递数据提供依赖需要使用procide(name,value)函数该函数有两个参数参数name表示的是传递数据的名称依赖名称可以是一个字符串或者一个Symbol后代组件需要通过依赖名称来查找注入数据的值参数value表示传递数据的值依赖值可以是任意类型的数据可以是一个数据常量也可以是一个响应式变量ref。
如果注入的数据值是一个响应式变量则后代组件在使用该数据时依旧具有响应式状态。
一个组件可以多次调用 provide()使用不同的依赖名称注入不同的依赖值。
script setup
// 导入需要使用的API
import { ref, provide } from vue// 声明一个响应式变量
let count ref(0);
// 声明一个普通变量
const text 一个常量// 使用provide()注入一个响应式变量
provide(count,count);
// 使用provide()注入一个普通js变量
provide(text,text);
/script全局provide
除了在组件中进行依赖注入还可以在应用层面进行依赖注入相当于注入了一个全局的依赖该依赖可以在应用中的所有组件中注入使用。全局依赖注入需要借助main.js中createApp()创建的应用实例
// main.js中导入API
import { createApp, ref } from vue
// 其他。。。// 创建应用实例
const app createApp(App)
// 声明响应式变量
let test ref(哈哈哈)
// provide() 传递常量
app.provide(all, 123)
// provide() 传递响应式变量
app.provide(test, test)// 其他。。。 想要声明全局变量除了使用依赖注入之外还可以利用app.config.globalProperties声明全局变量。
3、inject注入依赖
上层组件通过provide()提供依赖之后后代组件需要通过inject(name[,default,boolean])函数来注入依赖将上层组件传递的数据接收到当前组件中来。该函数只有一个必选参数name表示要注入依赖的名称也就是上层组件中provide()的name参数
script setup
// 导入需要使用的API
import { inject } from vue;// 注入响应式变量依赖
let count inject(count)
console.log(count);
// 注入普通变量依赖
let text inject(text)
console.log(text);
// 注入全局普通变量依赖
let all inject(all)
console.log(all);
// 注入全局响应式依赖
let test inject(test)
console.log(test);
/script控制台输出 根据上面控制台的输出结果可以看出如果上层组件提供的依赖是一个响应式变量ref则通过inject()注入的依赖将是该ref对象不会被解包为变量值这种方式保持了ref在上层组件和后代组件之间的响应式变化。
默认值
默认情况下后代组件中inject()注入的依赖必然是上层组件中已经提供的依赖否则会抛出一个运行时警告。但如果并不要求注入的依赖被提供过则可以给依赖设置一个默认值。当inject()未能从上层组件获取到依赖值时就会将默认值作为依赖的值。这也就是inject()函数的第一个可选参数
// 注入一个未提供的依赖 并设置默认值
let test2 inject(test2,默认值)
console.log(test2); // 默认值 如果默认值需要调用一个函数或者初始化一个类来设置为了避免在用不到默认值的情况下造成的性能消耗可以使用工厂函数来创建默认值并通过inject()函数的第二个可选参数表明第二个参数为一个工厂函数
// 利用工厂函数设置默认值
let test3 inject(test3,() new Object(),true)
console.log(test3);4、响应式变量的依赖注入
我们在上层组件中可以将响应式变量ref作为提供的依赖后代组件将该依赖注入之后也就获取到该ref自然也可以在后代组件中修改该ref的值但这样会影响到所有注入该依赖的组件而且逻辑比较混乱。所以针对响应式数据建议将所有对响应式数据的更新修改操作都集中保持在提供依赖的组件中。
即使需要在后代组件中更新修改响应式数据的值也可以可以通过在提供依赖的组件中声明并提供一个更新数据的方法。
提供依赖的上层组件
script setup
// 导入需要使用的API
import { ref, provide } from vue
// 声明一个响应式变量
let count ref(0);
// 声明响应式依赖的更新方法
const changeCount (n) {count.valuen
}// 使用provide()注入响应式变量
provide(count,count);
// 使用provide()注入响应式变量的更新方法
provide(changeCount,changeCount)
/script注入依赖的后代组件
script setup
// 导入需要使用的API
import { inject } from vue;// 注入响应式变量依赖
let count inject(count)
// 注入响应式变量依赖的更新方法
let changeCount inject(changeCount)
// 其他。。。
// 调用更新方法修改响应式变量的值
changeCount(1);
// 其他。。。
/script5、readonly()
如果想要确保上层组件中提供的依赖数据不被注入依赖的后代组件所修改则需要借助readonly()函数来设置依赖只读。如果设置只读的是响应式变量ref或着复杂数据类型的JS变量则在后代组件中尝试修改时上层组件和后代组件中的变量值都不会被修改如果设置只读的是简单数据类型的JS变量则在后代组件中尝试修改时上层组件的变量值不变下层组件的变量值会变。
因为传递响应式变量和复杂数据类型的JS变量是将本身的引用传递过去了上层组件和后代组件中引用的同一个ref而简单数据类型的JS变量则将在后代组件中新建了一份变量与上层组件中的变量并非同一个。
提供依赖的上层组件
script setup
// 导入需要使用的API
import { ref, provide, readonly } from vue
// 声明一个响应式变量
let count ref(0);
// 声明一个普通变量
const text 一个常量// 使用provide()注入一个响应式变量
provide(count,readonly(count));
// 使用provide()注入一个普通js变量
provide(text,readonly(text));
/script注入依赖的后代组件
script setup
// 导入需要使用的API
import { inject } from vue;// 注入响应式变量依赖
let count inject(count);
console.log(count.value); // 0
count.value;
console.log(count.value); // 0
// 注入普通变量依赖
let text inject(text)
console.log(text); // 一个常量
text 2222
console.log(text); // 222 但上层组件中依旧是text
/script6、依赖名称冲突
如果上层组件中在提供依赖时多个依赖使用的同一个依赖名称则最终后代组件在注入依赖时获取的数据将是最后一个使用该依赖名称的数据因为后面的数据会将前面的数据覆盖。。
在构建大型项目时为了避免依赖名称的冲突可以使用Symbol作为依赖的名称因为Symbol数据永远不会重复。将所有Symbol存储到一个单独的js文件中并将Symbol导出这样方便Symbol在上层组件和后代组件的中使用。
用来存储Symbol的js文件
// 将Symbol数据导出
export const oneKey Symbol()提供依赖的上层组件
script setupimport { provide } from vueimport { oneKey } from ./keys.js// 向后代组件提供依赖provide(oneKey, 要提供的数据);
/script注入依赖的后代组件
script setupimport { inject } from vueimport { oneKey } from ./keys.js// 向当前组件注入依赖const test inject(oneKey);
/script 更多Symbol相关内容请查看JavaScript 之 Symbol 数据类型。
二、异步组件
1、简介
异步组件是指以异步的方式去加载组件这些组件不会在页面初始加载时立即加载而是在需要渲染到DOM上的时候才加载从而可以提高应用的加载速度和性能。
Vue提供了defineAsyncComponent()方法用来实现异步组件的功能。
2、基本用法
defineAsyncComponent()方法的参数是一个返回Promise的加载函数当从服务器获取组件成功时调用resolve()加载失败时调用reject()。加载成功之后该方法的返回值是一个包装过的组件使用变量接收返回值后就可以通过变量在组件模板中使用异步加载的组件了。
script setup
// 导入要使用的方法
import { defineAsyncComponent } from vue// 调用方法 异步加载组件 并将加载的组件赋值给AsyncComp
const AsyncComp defineAsyncComponent(() {return new Promise((resolve, reject) {// ...从服务器获取组件// 加载成功resolve(/* 获取到的组件 */)// 加载失败reject(/* 加载失败 */)})
})
/scripttemplatediv!-- 正常使用AsyncComp组件 --AsyncComp //div
/template3、ES模块动态导入
ES模块动态导入import其实也会返回一个Promise所以可以动态导入Vue单文件组件并与defineAsyncComponent()方法搭配使用实现组件的异步加载。异步加载获取的是一个包装过的组件它会将接收到的props和插槽、事件监听器等内容传递给内部加载的异步组件。
Vite 和 Webpack等构建工具也都支持该语法。
templatedivdiv v-ifshowAsyncChild //div/div
/templatescript setup
// 导入需要使用的API
import { ref, defineAsyncComponent } from vue
// 声明响应式变量
let show ref(false)
// 设置定时器 修改响应式变量的值
setTimeout(() {show.value true;
},3000)// 异步加载组件 会在页面渲染 AsyncChild 时 调用执行
const AsyncChild defineAsyncComponent(() {console.log(加载异步组件);return import(../components/Child3.vue)
})
/script与普通组件一样异步组件也支持通过app.component()注册为全局组件
// main.js
app.component(AsyncChild, defineAsyncComponent(() import(../components/Child3.vue)
))4、加载与错误状态
既然是异步加载那就必然会涉及到加载中和加载失败的状态defineAsyncComponent() 方法在高级选项中提供了处理这些状态方法包括加载状态显示、加载错误兜底展示等内容
const AsyncComp defineAsyncComponent({// 异步加载组件函数 loader: () import(../components/Child3.vue),// 加载中状态 展示的组件loadingComponent: LoadingComponent,// 展示加载组件前的延迟时间默认为 200ms 避免加载太快导致的闪烁问题delay: 200,// 加载失败状态 展示的组件errorComponent: ErrorComponent,// 可以指定 timeout 时间限制超时后也会显示这里配置的报错组件默认值是Infinity 无限timeout: 3000
})5、Suspense
异步组件可以搭配内置的 Suspense 组件一起使用但Suspense 目前还是一项实验性功能因此这里就不展开介绍了感兴趣的小伙伴可以查阅相应文档Suspense。