Vue2.x-基础梳理(二)

59 阅读5分钟

本系列文章是对vue2.x的知识进行系统梳理,也算是一个回顾。

本系列只针对基础知识,难度较浅,如有错误,欢迎大佬指正,不胜感激!!!

组件
使用组件

Vue中使用组件的三大步骤:

一、定义组件

  • 使用Vue.extend(options)创建,其中optionsnew Vue(options)时传入的几乎一样,但有如下区别:
    • el不需要写,因为所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
    • data必须写成函数, 避免组件被复用时,数据存在引用关系。
    • 使用template配置组件结构。

二、注册组件

  • 局部注册:new Vue()的时候传入components选项
  • 全局注册:使用Vue.component('组件名',组件)

三、使用组件(写组件标签):

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>第一个组件</title>
		<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<hello></hello>
			<hr>
			<!-- 编写组件标签 -->
			<home></home>
		</div>
	</body>

	<script type="text/javascript">
		// 创建home组件
		const home = Vue.extend({
			template:`
				<div>
					<h2>名称:{{name}}</h2>
					<h2>地址:{{address}}</h2>
					<button @click="showName">点我提示名称</button>	
				</div>
			`,
			data(){
				return {
					name:'理发店',
					address:'小鸡岛'
				}
			},
			methods: {
				showName(){
					alert(this.name)
				}
			},
		})
		
		// 创建hello组件
		const hello = Vue.extend({
			template:`
				<div>	
					<h2>你好啊!{{name}}</h2>
				</div>
			`,
			data(){
				return {
					name:'伍六七'
				}
			}
		})
		// 全局注册组件
		Vue.component('hello',hello)

		// 创建vm
		new Vue({
			el:'#root',
			// 注册组件(局部注册)
			components:{
				home
			}
		})

	</script>
</html>
几个注意点

1.关于组件名:

  • 一个单词组成:
    • 第一种写法(首字母小写):home
    • 第二种写法(首字母大写):Home
  • 多个单词组成:
    • 第一种写法(kebab-case命名):my-home
    • 第二种写法(CamelCase命名):MyHome(需要Vue脚手架支持)
  • 备注:
    • (1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
    • (2).可以使用name配置项指定组件在开发者工具中呈现的名字。

2.关于组件标签:

  • 第一种写法:<home></home>
  • 第二种写法:<home/>

3.简写方式:const home = Vue.extend(options) 可简写为:const home = options

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>第一个组件</title>
		<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<home></home>
		</div>
	</body>

	<script type="text/javascript">
		// 创建home组件
		const home = {
			template:`
				<div>
					<h2>名称:{{name}}</h2>
					<h2>地址:{{address}}</h2>
					<button @click="showName">点我提示名称</button>	
				</div>
			`,
			data(){
				return {
					name:'理发店',
					address:'小鸡岛'
				}
			},
			methods: {
				showName(){
					alert(this.name)
				}
			},
		}

		// 创建vm
		new Vue({
			el:'#root',
			// 注册组件(局部注册)
			components:{
				home
			}
		})

	</script>
</html>
关于VueComponent:
  1. home组件本质是一个名为VueComponent的构造函数,不是程序员定义的,是Vue.extend生成的。
  2. 我们只需要写<home/><home></home>,Vue解析时会帮我们创建home组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
  3. 每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
  4. 关于this指向:
  • (1).组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是VueComponent实例对象
  • (2).new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是Vue实例对象
  1. VueComponent的实例对象,简称vc(也可称之为:组件实例对象)。Vue的实例对象,简称vm
重要的内置关系:
  1. 重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
  2. 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
Render函数和Template

在平时编程时,大部分是通过template来创建html。但是在一些特殊的情况下,使用template方式时,就无法很好的满足需求,在这个时候就需要 通过JavaScript 的编程能力来进行操作。此时,就到了render函数展示拳脚去时候了。

render函数

render函数的作用是,当场景中用 template 实现起来代码冗长繁琐而且有大量重复,这个时候使用就可以极大的简化代码。 在使用render函数中,会使用到一个参数createElement,而这个createElement参数,本质上,也是一个函数,是vue中构建虚拟dom所使用的工具

createElement(,{},[])方法中,有三个参数:

  • 第一个参数(必要参数):主要是用于提供dom中的html内容,类型可以是字符串、对象或函数。
  • 第二个参数(对象类型,可选):用于设置这个dom中的一些样式、属性、传的组件的参数、绑定事件之类的。
  • 第三个参数(类型是数组,数组元素类型是VNode,可选):主要用于设置分发的内容,如新增的其他组件。 注意:组件树中的所有vnode必须是唯一的 通过传入createElement参数,创建虚拟节点,然后再将节点返回给render返回出去。总的来说,render函数的本质就是创建一个虚拟节点。
render和template的区别

相同之处:

  • render 函数 跟 template 一样都是创建 html 模板

不同之处:

  • Template适合逻辑简单,render适合复杂逻辑。
  • 使用者template理解起来相对容易,但灵活性不足;自定义render函数灵活性高,但对使用者要求较高。
  • render的性能较高,template性能较低。
  • 使用render函数渲染没有编译过程,相当于使用者直接将代码给程序。所以,使用它对使用者要求高,且易出现错误
  • Render 函数的优先级要比template的级别要高,但是要注意的是Mustache(双花括号)语法就不能再次使用

注意:template和render不能一起使用,否则无效

render举例

如一次封装一套通用按钮组件,按钮有四个样式(success、error、warning、default)。 template方式是如下:

<div class="btn btn-success" v-if="type === 'success'">{{text}}<div>
<div class="btn btn-error" v-else-if="type === 'error'">{{text}}<div>
<div class="btn btn-warning" v-else-if="type === 'warning'">{{text}}<div>
<div class="btn btn-default" v-else="type === 'default'">{{text}}<div>

这样写在按钮少的时候没有问题,但是一旦按钮数量变多,这样写就会显得特别冗长,在这个时候,就需要render函数。 在使用render函数前,需要先把template标签去掉,只保留逻辑层。 通过传入的type动态填入class,通过inderText将内容添加入DOM中。

render(h){
  return h('div', {
    class: {
      btn: true,
      'btn-success': this.type === 'success',
      'btn-error': this.type === 'error',
			'btn-warning': this.type === 'warning',
			'btn-default': this.type === 'default',
    },
    domProps: {
      innerText: this.text
    },
    on: {
      click: this.handleClick
    }
  })
}
ref属性
<template>
	<div>
		<h1 v-text="msg" ref="title"></h1>
		<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
		<Home ref="home"/>
	</div>
</template>

<script>
	// 引入Home组件
	import Home from './components/Home'

	export default {
		name:'App',
		components:{School},
		data() {
			return {
				msg:'我是伍六七!'
			}
		},
		methods: {
			showDOM(){
				console.log(this.$refs.title) //真实DOM元素
				console.log(this.$refs.btn) //真实DOM元素
				console.log(this.$refs.home) //Home组件的实例对象(vc)
			}
		},
	}
</script>
props配置

父组件向子组件传值

<!--父组件-->
<template>
	<div>
		<Home name="伍六七" sex="男" :age="18"/>
	</div>
</template>
<script>
	import Home from './components/Home'

	export default {
		name:'App',
		components:{Home}
	}
</script>

<!--子组件-->
<template>
	<div>
		<h1>{{msg}}</h1>
		<h2>姓名:{{name}}</h2>
		<h2>性别:{{sex}}</h2>
		<h2>年龄:{{myAge}}</h2>
		<button @click="updateAge">修改年龄</button>
	</div>
</template>
<script>
	export default {
		name:'Home',
		data() {
			return {
				msg:'我是小鸡岛居民',
				myAge: this.age
			}
		},
		methods: {
			updateAge(){
				this.myAge++
			}
		},
		// 写法一:简单声明接收
		// props:['name','age','sex'] 

		// 写法二:接收的同时对数据进行类型限制
		/* 
		props:{
			name:String,
			age:Number,
			sex:String
		} 
		*/

		// 写法三:接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
		props:{
			name:{
				type: String, //name的类型是字符串
				required: true, //name是必要的
			},
			age:{
				type: Number,
				default: 10 //默认值
			},
			sex:{
				type: String,
				required: true
			}
		}
	}
</script>
minins混入

混入 (mixins): 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。

混入策略:

  1. 数据对象data在内部会进行浅合并 (一层属性深度),在和组件的数据发生冲突时以组件数据优先。
  2. 同名钩子函数将混合为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
  3. 值为对象的选项,例如 methods, components,将被混合为同一个对象。两个对象键名冲突时,取组件对象的键值对。

与vuex的区别

  • vuex:用来做状态管理的,里面定义的变量在每个组件中均可以使用和修改,在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之修改。
  • Mixins:可以定义共用的变量,在每个组件中使用,引入组件中之后,各个变量是相互独立的,值的修改在组件中不会相互影响。

与公共组件的区别

  • 公共组件:在父组件中引入组件,相当于在父组件中给出一片独立的空间供子组件使用,然后根据props来传值,但本质上两者是相对独立的。
  • Mixins:则是在引入组件之后与组件中的对象和方法进行合并,相当于扩展了父组件的对象与方法,可以理解为形成了一个新的组件。
插件

定义插件

// plugin.js
export default {
	install(Vue,x,y,z){
		console.log(x,y,z)
		//全局过滤器
		Vue.filter('mySlice',function(value){
			return value.slice(0,4)
		})

		//定义全局指令
		Vue.directive('fbind',{
			//指令与元素成功绑定时(一上来)
			bind(element,binding){
				element.value = binding.value
			},
			//指令所在元素被插入页面时
			inserted(element,binding){
				element.focus()
			},
			//指令所在的模板被重新解析时
			update(element,binding){
				element.value = binding.value
			}
		})

		//给Vue原型上添加一个方法(vm和vc就都能用了)
		Vue.prototype.hello = ()=>{alert('你好啊')}
	}
}

应用插件

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
import plugins from './plugins'

//应用(使用)插件
Vue.use(plugins,1,2,3)
//创建vm
new Vue({
	el:'#app',
	render: h => h(App)
})

使用插件功能

<template>
	<div>
		<h2>名称:{{name | mySlice}}</h2>
		<h2>地址:{{address}}</h2>
    <input type="text" v-fbind:value="name">
		<button @click="test">点我测试一个插件的hello方法</button>
	</div>
</template>

<script>
	export default {
		name:'home',
		data() {
			return {
				name:'理发店567',
				address:'小鸡岛',
			}
		},
		methods: {
			test(){
				this.hello()
			}
		},
	}
</script>
scoped样式

**scoped的作用功能:**组件样式私有化,不对全局造成样式污染,表示当前style属性只属于当前模块。 原理 通过观察DOM结构可以发现:vue通过在DOM结构以及css样式上添加唯一的标记,确保唯一,达到样式私有化,不污染全局的作用。样式属性上也会多一个该字符,确保唯一。

<button data-v-4780f187 type="button" name="button">添加<button>
<button data-v-4780f187 type="button" name="button">修改<button>
<button data-v-4780f187 type="button" name="button">删除<button>

可以看出,添加scoped后的组件里的标签都会多一个data-v-4780f187的属性,并且在css样式部分可以看出来:

button[data-v-4780f187]{
  background: red
}

由此可知,添加scoped属性的组件,为了达到不污染全局,做了如下处理:

  • 给HTML的DOM节点加一个不重复属性data-v-4780f187标志唯一性
  • 在添加scoped属性的组件的每个样式选择器后添加一个等同"不重复属性"相同的字段,实现类似于"作用域"的作用,不影响全局。
  • 如果组件内部还有组件,只会给最外层的组件里的标签加上唯一属性字段,不影响组件内部引用的组件。

慎用原因:

从原理可见,之所以scoped可达到类似组件私有化、样式设置"作用域"的效果,其实只是在设置scoped属性的组件上的所有标签添加data开头的属性,且在标签选择器的结尾加上和属性相同的字段,起到唯一性的作用,但是这样如果组件中也引用其他组件就会出现类似下面的问题:

  • 父组件无scoped属性,子组件带有scoped,父组件是无法操作子组件的样式的,虽然我们可以在全局中通过该类标签的标签选择器设置样式,但会影响到其他组件
  • 父组件有scoped属性,子组件没有。父组件也无法设置子组件样式,因为父组件的所有标签都带有data-v-469af010唯一标志,但子组件不会带有这个唯一标志属性,与1同理,虽然我们可以在全局中通过该类标签的标签选择器设置样式,但会影响到其他组件
  • 父子组件都有,同理也无法设置样式,更改起来增加代码质量。
自定义事件(子组件向父组件传递数据)

方式一:通过父组件给子组件传递函数类型的props实现:子给父传递数据

<!--父组件-->
<template>
	<div class="app">
		<h1>我是:{{name}}</h1>
		<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
		<Home :getAddress="getAddress"/>
	</div>
</template>
<script>
	import Home from './components/Home'
	export default {
		name:'App',
		components:{Home},
		data() {
			return {
				name:'伍六七'
			}
		},
		methods: {
			getAddress(addr){
				console.log('收到了地址:',addr)
			}
		}
	}
</script>

<!--子组件-->
<template>
	<div>
		<h2>地址:{{address}}</h2>
		<button @click="sendAddr">给父组件发送地址</button>
	</div>
</template>
<script>
	export default {
		name:'home',
		props:['getAddress'],
		data() {
			return {
				address:'玄武国'
			}
		},
		methods: {
			sendAddr(){
				this.getAddress(this.address)
			}
		},
	}
</script>

方法二:通过父组件给子组件绑定一个自定义事件实现:

<!--父组件-->
<template>
	<div class="app">
		<h1>我是:{{name}}</h1>
		<Home @getAddress="getAddress"/>
	</div>
</template>
<script>
	import Home from './components/Home'
	export default {
		name:'App',
		components:{Home},
		data() {
			return {
				name:'伍六七'
			}
		},
		methods: {
			getAddress(addr){
				console.log('收到了地址:',addr)
			}
		}
	}
</script>

<!--子组件-->
<template>
	<div>
		<h2>地址:{{address}}</h2>
		<button @click="sendAddr">给父组件发送地址</button>
    <button @click="unbind">解绑getAddress事件</button>
    <button @click="death">销毁当前Home组件的实例(vc)</button>
	</div>
</template>
<script>
	export default {
		name:'home',
		data() {
			return {
				address:'玄武国'
			}
		},
		methods: {
			sendAddr(){
        this.$emit('getAddress',this.address)
			},
      unbind(){
        // 解绑一个自定义事件
        // 传字符串解绑一个,传数组解绑多个,不传解绑所有
				this.$off('getAddress') 
			},
			death(){
        // 销毁了当前Home组件的实例,销毁后所有Home实例的自定义事件全都不奏效。
				this.$destroy() 
			}
		},
	}
</script>

方法三:通过父组件给子组件绑定一个自定义事件实现:子给父传递数据

<!--父组件-->
<template>
	<div class="app">
		<h1>我是:{{name}}</h1>
		<Home ref='home' @getAddress="getAddress"/>
	</div>
</template>
<script>
	import Home from './components/Home'
	export default {
		name:'App',
		components:{Home},
		data() {
			return {
				name:'伍六七'
			}
		},
    mounted() {
			this.$refs.home.$on('getAddress',this.getAddress) //绑定自定义事件
		},
		methods: {
			getAddress(addr){
				console.log('收到了地址:',addr)
			}
		}
	}
</script>

<!--子组件-->
<template>
	<div>
		<h2>地址:{{address}}</h2>
		<button @click="sendAddr">给父组件发送地址</button>
    <button @click="unbind">解绑getAddress事件</button>
    <button @click="death">销毁当前Home组件的实例(vc)</button>
	</div>
</template>
<script>
	export default {
		name:'home',
		data() {
			return {
				address:'玄武国'
			}
		},
		methods: {
			sendAddr(){
        this.$emit('getAddress',this.address)
			},
      unbind(){
        // 解绑一个自定义事件
        // 传字符串解绑一个,传数组解绑多个,不传解绑所有
				this.$off('getAddress') 
			},
			death(){
        // 销毁了当前Home组件的实例,销毁后所有Home实例的自定义事件全都不奏效。
				this.$destroy() 
			}
		},
	}
</script>
全局事件总线($bus-组件间通信)

事件总线$bus主要使用vue高级APIvm.$on原理,例:

// main.js
Vue.prototype.$bus = new Vue(); // $bus是原型对象上的实例
// child1
this.$bus.$on('foo', handle)  //子组件通过$bus监听事件
// child2
this.$bus.$emit('foo')  //子组件通过$emit触发事件

以上写法等同于以下写法:

class Bus {
  constructor() {
    this.callbacks = {}
  }
  $on(name, fn) {
    this.callbacks[name] = this.callbacks[name] || []
    this.callbacks[name].push(fn)
  }
  $emit(name, args) {
    if (this.callbacks[name]) {
      this.callbacks[name].forEach(cb => cb(args))
    }
  }
}
// main.js
Vue.prototype.$bus = new Bus()
// child1
this.$bus.$on('foo', handle)
// child2
this.$bus.$emit('foo')
v-on(@)与$on的区别

v-on:

  • 可监听普通dom的原生事件;
  • 可监听子组件emit的自定义事件; vm.$on:
  • 监听当前实例的自定义事件
$nextTick

vue中有nextTick这个API,官方解释,它可以在DOM更新完毕之后(页面渲染之前)执行一个回调。

作用:

  1. Vue是异步渲染的框架。
  2. data改变之后,DOM不会立刻渲染。
  3. $nextTick会在DOM修改之后被触发,以获取最新的DOM节点。
  4. 连续多次的异步渲染,$nextTick只会执行最后一次渲染后的结果。

原理:

$nextTick主要通过事件循环中的任务队列的方式异步执行传入的回调函数,首先会判断当前的执行环境是否支持Promise,MutationObserver,setImmediate,setTimeout。如果支持则创建对应的异步方法,这里的MutationObserver并不是监听DOM,而是利用其微任务特性。需要注意的是更新DOM的方法也是通过nextTick进行调用的,因此就可以实现传入$.nextTick的回调函数在DOM渲染完成之后执行这些微任务。

Vue处于性能考虑,Vue会将用户同步修改的多次数据缓存起来,等同步代码执行完,说明这一次的数据修改就结束了,然后才会去更新对应DOM,一方面可以省去不必要的DOM操作,比如同时修改一个数据多次,只需要关心最后一次就好了,另一方面可以将DOM操作聚集,提升render性能。

vue-01.jpg

涉及到微任务(microtask)和宏任务(macrotask)

microtask有:Promise、MutationObserver,以及nodejs中的process.nextTick

macrotask有:setTimeout, setInterval, setImmediate, I/O, UI rendering

插槽(slot)
默认插槽
<!--定义插槽-->
<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
	</div>
</template>
<script>
	export default {
		name:'Category',
		props:['title']
	}
</script>

<!--使用插槽-->
<template>
	<div class="container">
		<Category title="美食" >
			<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
		</Category>

		<Category title="游戏" >
			<ul>
				<li v-for="(g,index) in games" :key="index">{{g}}</li>
			</ul>
		</Category>

		<Category title="电影">
			<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
		</Category>
	</div>
</template>
<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category},
		data() {
			return {
				foods:['火锅','烧烤','小龙虾','牛排'],
				games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
				films:['《教父》','《拆弹专家》','《你好,李焕英》']
			}
		},
	}
</script>
具名插槽
<!--定义插槽-->
<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
		<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
	</div>
</template>
<script>
	export default {
		name:'Category',
		props:['title']
	}
</script>

<!--使用插槽-->
<template>
	<div class="container">
		<Category title="美食" >
			<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
		</Category>

		<Category title="游戏" >
			<ul slot="center">
				<li v-for="(g,index) in games" :key="index">{{g}}</li>
			</ul>
			<div class="foot" slot="footer">
				<a href="http://www.atguigu.com">单机游戏</a>
				<a href="http://www.atguigu.com">网络游戏</a>
			</div>
		</Category>

		<Category title="电影">
			<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
      <!--完整写法
			<template v-slot:footer>
			-->
      <!--简写-->
      <template #footer>
				<div class="foot">
					<a href="http://www.atguigu.com">经典</a>
					<a href="http://www.atguigu.com">热门</a>
					<a href="http://www.atguigu.com">推荐</a>
				</div>
				<h4>欢迎前来观影</h4>
			</template>
		</Category>
	</div>
</template>
<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category},
		data() {
			return {
				foods:['火锅','烧烤','小龙虾','牛排'],
				games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
				films:['《教父》','《拆弹专家》','《你好,李焕英》']
			}
		},
	}
</script>
作用域插槽

为预留的 <slot> 插槽绑定props 数据,这种带有props 数据的 <slot> 叫做“作用域插槽”

slot-scope(2.6版本之前)
v-slot(2.6版本之后)
<!--
定义插槽
插槽向外传递数据games,由插槽使用者处理
-->
<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<slot :games="games" msg="hello">我是默认的一些内容</slot>
	</div>
</template>
<script>
	export default {
		name:'Category',
		props:['title'],
		data() {
			return {
				games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
			}
		},
	}
</script>

<!--
使用插槽
在外部使用插槽内部传递的数据
-->
<template>
	<div class="container">

		<Category title="游戏">
			<template scope="scopeName">
				<ul>
					<li v-for="(g,index) in scopeName.games" :key="index">{{g}}</li>
				</ul>
			</template>
		</Category>

		<Category title="游戏">
			<template scope="{games}">
				<ol>
					<li style="color:red" v-for="(g,index) in games" :key="index">{{g}}</li>
				</ol>
			</template>
		</Category>

		<Category title="游戏">
			<template slot-scope="{games}">
				<h4 v-for="(g,index) in games" :key="index">{{g}}</h4>
			</template>
		</Category>

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