1. axios 介绍


axios是基于promise风格的。ES6语法的promise风格。

js中有两个原生发送请求的对象,分别是xhr和fetch。fetch也是基于promise风格的。但是用的最多的还是xhr。

但是目前而言,像jQuery的$get,$post,$ajax等 和 axios都是基于xhr的!


axios的安装:
在这里插入图片描述


针对post发送表单格式的参数,必须要使用FormData来操作。
在这里插入图片描述

2. CORS(cross-origin resource sharing) 同源策略


出现这个跨域资源共享的问题就说明一件事,违背了同源的原则。

在这里插入图片描述

原因:因为这是浏览器安全策略(同源策略),专门设计用来避免恶意的Javascript访问你的web页面和用户的cookie。所以呢,如果你的要访问的数据跟你的不是在同一个服务器下,使用XMLHTTPRequest是会被拦截住的。


解决CORS跨域的几种方式:

  • 第一种方式:后台人员添加允许跨域的响应头。服务器端设置好对应的允许跨域的响应头,我们浏览器端就可以接受。
//设置跨域问题。
//特定的方法或者ip名,自己定义。
HttpServletResponse resp = (HttpServletResponse)response;
resp.addHeader("Access-Control-Allow-Origin", "*");
resp.addHeader("Access-Control-Allow-Headers", "*");
resp.addHeader("Access-Control-Allow-Methods", "*");
resp.addHeader("Access-Control-Max-Age", "3600");
resp.addHeader("Access-Control-Allow-Credentials", "false");
  • 第二种方式:jsonp格式,jsonp的本质是通过操作script的src属性,url拼接上对应后台的参数名和对应值来获取的。正因如此,所以只能支持get请求的。这种方式很少用,但是面试容易问!
//第一步,定义动态script
var script = document.createElement("script");
//这里写了url路径和参数参数值格式,最后添加了一个callback来定义方法名。
script.src = "url(路径)?参数=" + 参数值 + "&callback=fun";
script.type = "text/javascript";

//第二步,指定的回调函数
window["fun"] = function(data){
	console.log(data);
};

//第三部,将script放到head元素下面。
var head = document.querySelector("head");
head.appendChild(script);
  • 第三种方式:代理服务器,在自己本地也配置一台代理服务器。

  • 代理服务器存在前端服务器的,并且他的端口号域名都和前端服务器对应上。让代理服务器和后台服务器进行通信,这样前端服务器页面直接通过访问代理服务器来获取数据可以了,就不存在跨域问题。

  • 很多人疑问代理服务器和后台服务器之前难道不会有跨域问题?

    • 答:不会有跨域问题,因为代理服务器是服务器并不是在浏览器端的,代理服务器和后台服务器,是服务器与服务器之间打交道,是http协议!中间可没有什么浏览器的同源策略。
    • 代理服务器既然是和前端服务器在一起的,也是nodejs环境,js写的服务器。也就是js服务器和后台服务器通过http协议进行数据通信。
    • 此外,XmlHttpRequest对象是在前端浏览器中的。nodejs环境下的服务器是没有这个对象的。
      在这里插入图片描述

3. Vue的代理服务器 方式一


在这里插入图片描述

3.1 方式一:配置一个代理服务器


在这里插入图片描述

那么对于中间这个代理服务器,我们可以通过什么来实现呢?

  • 通过nginx可以实现。
  • 通过vue-cli脚手架也可以实现。

一般我们vue开发就肯定用vue脚手架来配置代理服务器。

并且代理服务器也不需要我们自己创建,只需要更改vue脚手架的配置就可以了。

在这里插入图片描述


配置代理服务器和演示效果:

第一步:配置vue.config.js核心配置文件,设置为:

module.exports = {
	//关闭语法检查
	lintOnSave:false,
	//开启代理服务器,这台代理服务器默认就是和被代理的服务器的端口域名相同。
	//注意:重写了config文件就必须重新启动vue脚手架项目
	devServer: {
		//这里配置需要访问的后台服务器
	    proxy: 'http://39.103.163.156:8081/'
	}
}

第二步:检验和使用代理服务器,接受数据。

App.vue文件:

  • 这里我们也不会访问后台的地址了,而是直接访问代理服务器地址。注意要添加上访问后台的路径。
<template>
	<div id="root">
		<button @click="getStudents">获取学生信息</button>
	</div>
</template>

<script>
	import axios from 'axios'
	export default{
		name:'App',
		methods:{
			getStudents(){
				//这里直接访问代理服务器域名和端口(和脚手架服务器域名端口一样),并且要加上后台路径。
				axios.get('http://localhost:8080/marry/user/allUser').then(
					response => {
						//取值
						console.log("请求成功:",response.data)
					},
					error =>{
						//取错误信息
						console.log("请求失败了")
						console.log(error)
					}
				)
			}
		}
	}
</script>

在这里插入图片描述

3.2 方式一:代理服务器会出现两个问题


第一个问题,既然代理服务器和前端服务器的域名和端口号相同,那么页面访问的时候是访问的前端服务器还是代理服务器?

  • 我们访问的localhost:8080,是前端服务器的public目录下的信息。
    在这里插入图片描述

这样就会产生一个冲突,如果我访问的路径内容,在前段服务器中有!它就不会在去找代理服务器转发到后台服务器进行通信了。

  • 例如:http://localhost:8080/user/allUser , 我们通过代理服务器想要访问后台服务器的/user/allUser的接口,但是恰好前段服务器的public目录下方,也有一个/user/allUser目录。那么就直接返回前端服务器的东西了。

第二个问题,代理服务器只能配置匹配一个后台服务器不能配置多个后台服务器!
在这里插入图片描述

4. Vue的代理服务器 方式二


在这里插入图片描述

4.1 方式二:配置代理服务器


配置vue.config.js文件:

  • 理解前缀,注意前缀也是会被发到后台服务器的!因此我们需要配置pathRewrite的正则表达式来去掉前缀。
module.exports = {
	lintOnSave:false,
	//开启代理服务器,这台代理服务器默认就是和被代理的服务器的端口域名相同。
	devServer: {
		proxy: {
			
			//请求前缀。就是我们每次想要请求代理服务器的内容,可以在路径前面加一个前缀,方便识别。
			//这样可以灵活的控制,访问前端服务器还是走代理服务器。
			'/itholmes': {
				
				target: 'http://39.103.163.156:8081/',
				
				//如果不配置pathRewrite,那么也会将前缀带到后台服务其上面的。要注意!
				//这里是个正则表达式。
				pathRewrite:{'^/itholmes':''},
				
				//这里的ws是websocket,用于支持websocket的。
				ws: true,
				
				/*
					changeOrigin:
						false的情况下:实话实说,直接告诉后台服务器我本地的ip和地址,我来自哪里。
						true的情况下:撒谎,给后台服务器发送的请求头中的host值是后台服务器的ip和地址,相当于欺骗后台服务器了。
						
					一般我们都设置为true,因为服务器可能会限制如果不是当前ip端口的拒绝请求。这样我们也可以通过撒谎访问后台服务器。
					总而言是,它就是用来控制请求头中host的值。
				*/
				changeOrigin: true
			},
			
			//配置多个代理就继续像下面添加就可以了。
			'/abc':{
				target:'http://39.103.163.156:8080/',
				pathRewrite:{'^/abc':''},
			},
			
		}
	}
}

App.vue组件:

<template>
	<div id="root">
		<button @click="getStudents">获取学生信息1</button>
		<button @click="getStudents2">获取学生信息2</button>
	</div>
</template>

<script>
	import axios from 'axios'
	export default{
		name:'App',
		methods:{
			getStudents(){
				//这里直接访问代理服务器域名和端口(和脚手架服务器域名端口一样),并且要加上后台路径。
				axios.get('http://localhost:8080/itholmes/marry/user/allUser').then(
					response => {
						//取值
						console.log("请求成功:",response.data)
					},
					error =>{
						//取错误信息
						console.log("请求失败了")
						console.log(error)
					}
				)
			},
			getStudents2(){
				//这里直接访问代理服务器域名和端口(和脚手架服务器域名端口一样),并且要加上后台路径。
				axios.get('http://localhost:8080/abc/marry/user/allUser').then(
					response => {
						//取值
						console.log("请求成功:",response.data)
					},
					error =>{
						//取错误信息
						console.log("请求失败了")
						console.log(error)
					}
				)
			}
		}
	}
</script>

4.2 方式二配置的优缺点


优点:

  • 可以配置多个代理服务器。
  • 可以灵活的控制请求是否走代理服务器还是访问本地的前端服务器。

缺点:

  • 配置略微繁琐,请求资源时必须添加前缀。

5. 对于第三方库导入使用


js文件,直接通过npm安装,直接import引入就可以。

在这里插入图片描述


css文件:

  • 一种也是通过import引入css文件。
    在这里插入图片描述

  • 在这就是在public下面放入css文件,在index.html中引入。
    在这里插入图片描述

6. 调用第三方接口呈现页面数据 案例

6.1 调用Github用户接口


调用github接口案例:

  • https://api.github.com/search/users?q=xxx

main.js文件:

  • 安装全局事件总线
import Vue from "vue"
import App from "./App.vue"

Vue.config.productionTip = false;

new Vue({
	el:'#app',
	render:h=>h(App),
	//安装全局事件总线
	beforeCreate() {
		Vue.prototype.$bus = this;
	}
})

App.vue文件:

<template>
	<div class="container">
		<Search/>
		<List/>
	</div>
</template>

<script>
	import List from './components/List.vue'
	import Search from './components/Search.vue'
	export default{
		name:'App',
		components:{
			List,
			Search
		}
	}
</script>

List.vue文件:

<template>
	<div class="row">
		<!-- 写v-for的时候一定不要忘记写key值。 -->
		<div class="card" v-for="user in users" :key="user.login">
			<a :href="user.html_url" target="_blank">
				<img :src="user.avatar_url" style="width: 100px;">
			</a>
			<p class="card-text">{{user.login}}</p>
		</div>
	</div>
</template>

<script>
	export default{
		name:'List',
		data(){
			return {
				users:[]
			}
		},
		mounted() {
			this.$bus.$on('getUsers',(data)=>{
				// console.log("我是list,收到",data)
				this.users = data
			})
		}
	}
</script>

<style>
	.album{
		min-height: 50rem;
		padding-top: 3rem;
		padding-bottom: 3rem;
		background-color: #f7f7f7;
	}
	.card{
		float: left;
		width: 33.333%;
		padding: .75rem;
		margin-bottom: 2rem;
		border: 1px solid #efefef;
		text-align: center;
	}
	
	.card > img{
		margin-bottom: .75rem;
		border-radius: 100px;
	}
	
	.card-text{
		font-size: 85%;
	}
</style>

index.html页面:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width,initial-scale=1.0">
	<link rel="icon" href="<%= BASE_URL %>favicon.ico">
	<title><%= htmlWebpackPlugin.options.title %></title>
	<!-- 我们可以在public设置好,在index中引入。 -->
	<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
	  
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
	
    <div id="app"></div>

  </body>
</html>

6.2 数据显示模块(案例中的List) 正常开发中有四部分


对于我们平时的数据显示模块就像上面github案例上面的中的List组件, 正常开发中有四部分:

  • welcome欢迎页面模块,用户未进行任何操作的页面。
  • loading加载页面模块,用户查询了一些内容,中间显示一个写加载页面。
  • data页面模块,用户查询到的数据显示到页面。
  • error页面模块,如果出现错误应该显示的页面。

这就使用v-show加一些data判断条件就可以操作上面的内容。

  • 细节问题:对于操作四个模块显示不显示的data,可以用一个统一的对象来操作,这样就方便很多。
  • 四个模块的展现时机,取决于给List.vue发数据的人也就是Search.vue文件。就是展现时机也是由Search.vue决定的。

List.vue文件:

<template>
	<div class="row">
		<!-- 展示data页面模块 -->
		<div v-show="info.users.length" class="card" v-for="user in info.users" :key="user.login">
			<a :href="user.html_url" target="_blank">
				<img :src="user.avatar_url" style="width: 100px;">
			</a>
			<p class="card-text">{{user.login}}</p>
		</div>
		<!-- 展示欢迎页面模块 -->
		<h1 v-show="info.isFirst">欢迎使用!</h1>
		<!-- 展示加载页面模块 -->
		<h1 v-show="info.isLoading">加载中...</h1>
		<!-- 展示错误信息模块 -->
		<h1 v-show="info.errMsg">错误信息为:{{info.errMsg}}</h1>
	</div>
</template>

<script>
	export default{
		name:'List',
		data(){
			return {
				info:{
					isFirst:true,
					isLoading:false,
					errMsg:'',
					users:[]
				}
			}
		},
		mounted() {
			//我们不接受一堆上面的东西,isFirst,isLoading,errMsg,users一下子四个参数。这样可读性太差。
			//我们传入一个对象过来就可以了dataObj
			this.$bus.$on('updateListData',(dataObj)=>{
				console.log(dataObj)
				//我们直接使用info来统一管理这些模块的显示就可以了!
				
				// 这里不能直接等于容易丢失对象
				// this.info = dataObj
				
				// 我们可以通过字面量的合并这两个对象
				this.info = {...this.info,...dataObj}
			})
		}
	}
</script>

<style>
	.album{
		min-height: 50rem;
		padding-top: 3rem;
		padding-bottom: 3rem;
		background-color: #f7f7f7;
	}
	.card{
		float: left;
		width: 33.333%;
		padding: .75rem;
		margin-bottom: 2rem;
		border: 1px solid #efefef;
		text-align: center;
	}
	
	.card > img{
		margin-bottom: .75rem;
		border-radius: 100px;
	}
	
	.card-text{
		font-size: 85%;
	}
</style>

Search.vue文件:

<template>
	<section class="jumbotron">
		<h3 class="jumbotron-heading">Search Github Users</h3>
		<div>
			<input type="text" placeholder="enter the name you search" v-model="keyWord">&nbsp;
			<button @click="searchUsers">Search</button>
		</div>
	</section>
</template>

<script>
	import axios from 'axios'
	export default{
		name:'Search',
		data(){
			return {
				keyWord:''
			}
		},
		methods:{
			searchUsers(){
				
				//请求前更新List对应模块的数据
				console.log("请求前") 
				// List中四个模块的状态
				//与前面对应,不传入单个值,传入一个对象让函数接受是最好的!! 这样子语义化更好!!! 格式问题!!
				this.$bus.$emit('updateListData',{isFirst:false,isLoading:true,errMsg:'',users:[]})
				
				//使用es6的模板字符串, ` 参数用${}表示 ` 。
				axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
					response => {
						//请求成功后更新List对应模块的数据
						console.log("请求成功了") 
						// List中四个模块的状态
						this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items})
					}
				).catch(
					error => {
						//请求失败后更新List对应模块的数据
						console.log("发生错误了")
						// List中四个模块的状态
						this.$bus.$emit('updateListData',{isLoading:false,errMsg:error,users:[]})
					}
				)
				
			}
		}
	}
</script>

7. vue-resource 插件库


前端发送接受数据的几种方式:

  • xhr对象(原始方式)。
  • jQuery封装(基于xhr)。
  • axios操作(基于xhr)。
  • fetch(和xhr平级的一个)。
  • vue-resource插件库(使用的并不是很多,但是要了解,并且他也是基于xhr的)。

安装vue-resource插件库:
在这里插入图片描述
main.js文件:

  • 引入vue-resource插件。
import Vue from "vue"
import App from "./App.vue"
//导入vue-resource插件库
import vueResource from 'vue-resource'

Vue.config.productionTip = false;

//Vue使用插件
Vue.use(vueResource)

new Vue({
	el:'#app',
	render:h=>h(App),
	//安装全局事件总线
	beforeCreate() {
		Vue.prototype.$bus = this;
	}
})

在这里插入图片描述


vue-resource插件库:

  • 它也是promise风格的。
  • 使用规则和axios,fetch都差不多。

Search.vue组件

  • 导入完成只需要将axios该成this.$http就可以了,都是基于promise风格很方便。
<template>
	<section class="jumbotron">
		<h3 class="jumbotron-heading">Search Github Users</h3>
		<div>
			<input type="text" placeholder="enter the name you search" v-model="keyWord">&nbsp;
			<button @click="searchUsers">Search</button>
		</div>
	</section>
</template>

<script>
	export default{
		name:'Search',
		data(){
			return {
				keyWord:''
			}
		},
		methods:{
			searchUsers(){
				
				console.log(this)
				
				//请求前更新List对应模块的数据
				console.log("请求前") 
				// List中四个模块的状态
				//与前面对应,不传入单个值,传入一个对象让函数接受是最好的!! 这样子语义化更好!!! 格式问题!!
				this.$bus.$emit('updateListData',{isFirst:false,isLoading:true,errMsg:'',users:[]})
				
				//使用es6的模板字符串, ` 参数用${}表示 ` 。
				this.$http.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
					response => {
						//请求成功后更新List对应模块的数据
						console.log("请求成功了") 
						// List中四个模块的状态
						this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items})
					}
				).catch(
					error => {
						//请求失败后更新List对应模块的数据
						console.log("发生错误了")
						// List中四个模块的状态
						this.$bus.$emit('updateListData',{isLoading:false,errMsg:error,users:[]})
					}
				)
				
			}
		}
	}
</script>

总结:
在这里插入图片描述

8. Vue的 插槽使用

8.1 默认插槽


插槽的作用:

  • 让父组件可以像子组件指定位置插入html结构,也是一种组件间的通信的方式,适用于 父组件 ==> 子组件。

之前我们的操作都是操作组件标签的属性。现在我们要在组件标签体中设置相关标签,放到后台的默认插槽slot中。

App.vue文件:

<template>
	<div class="container">
		
		<!-- 我们在组件的标签体重设置内容,会默认放到slot标签中。 -->
		<Category title="美食">
			<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
		</Category>
		
		<Category title="游戏">
			<ul>
				<li v-for="(item,index) in games" :key="index">{{item}}</li>
			</ul>
		</Category>
		
		<Category title="电影">
			<!-- controls属性代表控制。 -->
			<video controls="controls" src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
		</Category>
		
	</div>
</template>

<script>
	import Category from './components/Category.vue'
	export default{
		name:'App',
		data(){
			return {
				foods:['火锅','烧烤','小龙虾'],
				games:['红色警戒','穿越火线','英雄联盟'],
				films:['abc','efg','功夫熊猫']
			}
		},
		components:{
			Category
		}
	}
</script>

<style lang="css">
	.container{
		display: flex;
		justify-content: space-around;
	}
	img{
		width: 100%;
	}
	video{
		width: 100%;
	}
</style>

Category.vue文件:

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 定义一个插槽,等待组件的使用者进行填充。 -->
		<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现。</slot>
	</div>
</template>

<script>
	export default{
		name:'Category',
		props:['listData','title']
	}
</script>

<style scoped>
	.category{
		background-color: skyblue;
		width: 200px;
		height: 300px;
	}
	h3{
		text-align: center;
		background-color: orange;
	}
</style>

8.2 具名插槽


具名插槽就是给插槽起名字:

  • <slot name=“center”></slot>和 slot=“center” 搭配使用。
  • <template v-slot:footer>也可以实现具名插槽效果,但是v-slot指令只能应用到组件标签和template标签上面。

App.vue文件:

<template>
	<div class="container">
		
		<Category title="美食">
			<!-- slot=""和组件对应的solt的name对应起来。-->
			<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
			<a slot="footer" href="">更多美食</a>
		</Category>
		
		<Category title="游戏">
			<ul slot="center">
				<li v-for="(item,index) in games" :key="index">{{item}}</li>
			</ul>
			<div slot="footer" class="foot">
				<a href="">单机游戏</a>
				<a href="">网络游戏</a>
			</div>
		</Category>
		
		<Category title="电影">
			<!-- controls属性代表控制。 -->
			<video slot="center" controls="controls" src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
			<!-- 想要不多添加div,可以使用template操作。注意这里v-slot只能用于template和组件标签上面。-->
			<template v-slot:footer>
				<div class="foot">
					<a href="">经典</a>
					<a href="">热门</a>
					<a href="">推荐</a>
				</div>
				<h4>欢迎前来观影</h4>
			</template>
		</Category>
		
	</div>
</template>

<script>
	import Category from './components/Category.vue'
	export default{
		name:'App',
		data(){
			return {
				foods:['火锅','烧烤','小龙虾'],
				games:['红色警戒','穿越火线','英雄联盟'],
				films:['abc','efg','功夫熊猫']
			}
		},
		components:{
			Category
		}
	}
</script>

<style lang="css">
	.container,.foot{
		display: flex;
		justify-content: space-around;
	}
	img{
		width: 100%;
	}
	video{
		width: 100%;
	}
	h4{
		text-align: center;
	}
</style>

Category.vue文件:

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 给插槽起名字 -->
		<slot name="center">slot1:我是一些默认值,当使用者没有传递具体结构时,我会出现。</slot>
		<br>
		<slot name="footer">slot2:我是一些默认值,当使用者没有传递具体结构时,我会出现。</slot>
	</div>
</template>

<script>
	export default{
		name:'Category',
		props:['listData','title']
	}
</script>

<style scoped>
	.category{
		background-color: skyblue;
		width: 200px;
		height: 300px;
	}
	h3{
		text-align: center;
		background-color: orange;
	}
</style>

8.3 作用域插槽


作用域插槽:

  • 本质上就是在子组件设置的data参数数据,可以作用到父组件定义该子组件的标签上面。类似一个提升作用域的效果。
  • 总结起来就是:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
  • 还有一个注意点:父组件接受数据对象时一定要是template标签。

Category.vue文件:

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!--将本组件中的games数据,以对象的形式传给父组件。-->
		<slot :youxi="games">
			我是的插槽默认的一些内容。
		</slot>
	</div>
</template>

<script>
	export default{
		name:'Category',
		props:['title'],
		data(){
			return {
				games:['红色警戒','穿越火线','英雄联盟'],
			}
		},
	}
</script>

<style scoped>
	.category{
		background-color: skyblue;
		width: 200px;
		height: 300px;
	}
	h3{
		text-align: center;
		background-color: orange;
	}
</style>

App.vue文件:

<template>
	<div class="container">
		
		<Category title="游戏">
			<!-- itholmes是作用域插槽发送过来的对象数据。 -->
			<template scope="itholmes">
				{{itholmes}}
				<ul>
					<li v-for="(item,index) in itholmes.youxi" :key="index">{{item}}</li>
				</ul>
			</template>
		</Category>
		
		<Category title="游戏">
			<!-- 可以直接从对象传过来的对象中拿到youxi参数。 -->
			<template scope="{youxi}">
				<ol>
					<li v-for="(item,index) in youxi" :key="index">{{item}}</li>
				</ol>
			</template>
		</Category>
		
		<Category title="游戏">
			<!-- 也可以使用slot-scope,功能和scope是一样的。 -->
			<template slot-scope="{youxi}">
				<h4 v-for="(item,index) in youxi" :key="index">{{item}}</h4>
			</template>
		</Category>
		
	</div>
</template>

<script>
	import Category from './components/Category.vue'
	export default{
		name:'App',
		components:{
			Category
		}
	}
</script>

<style lang="css">
	.container,.foot{
		display: flex;
		justify-content: space-around;
	}
	img{
		width: 100%;
	}
	video{
		width: 100%;
	}
	h4{
		text-align: center;
	}
</style>

8.4 总结


默认插槽:
在这里插入图片描述

具名插槽:
在这里插入图片描述
作用域插槽:
请添加图片描述

Logo

前往低代码交流专区

更多推荐