文章目录
组件
组件化的开发其实就是把页面拆分成一块一块的,每一块就是我们的一个组件
组件是可复用的 Vue 实例 - 组件中的选型基本上和vue实例保持一致
组件初体验
<body>
<div id="app">
<my-button ></my-button>
</div>
</body>
<script>
// 全局注册组件
// Vue.component('my-button', {
// template: `<h3>这里就是vue的组件 - {{ msg }}</h3>`,
// data () {
// return {
// msg: '112233'
// }
// }
// })
const app = new Vue({
el: '#app',
data: {
},
components: { // 局部注册组件
'my-button': {
template: `<h3>这里就是vue的组件 - {{ msg }}</h3>`,
data () {
return {
msg: '112233'
}
}
}
}
})
</script>
注册组件
全局注册组件
template
-
类型:string
-
详细:
-
一个字符串模板作为 Vue 实例的标识使用。模板将会 替换 挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发插槽。
-
如果值以 # 开始,则它将被用作选择符,并使用匹配元素的 innerHTML 作为模板。常用的技巧是用
<script type="x-template">
包含模板。
在new Vue实例之前 使用 Vue.component() 去注册组件
<script>
// 全局注册组件
// 一定是在new Vue实例之前
/**
Vue.component('component-name', { // 组件的配置
template: '', // 组件的结构
data () { // 组件的初始化数据,必须是函数
return {}
},
methods: {}, // 组件自定义事件
watch: {}, // 侦听属性
computed: {}, // 计算属性
mounted () {} // 生命周期钩子函数 - 都可以用
})
*/
// 组件名称在定义的时候可以写成
// Vue.component('component-name', {}) 连接符
// Vue.component('componentName', {}) 驼峰式命名
// 使用 <component-name></component-name>
const app = new Vue({
el: '#app'
})
</script>
实例代码
<body>
<div id="app">
<my-button ></my-button>
</div>
</body>
<script>
// 全局注册组件
// 一定是在new Vue实例之前
/**
Vue.component('component-name', { // 组件的配置
template: '', // 组件的结构
data () { // 组件的初始化数据,必须是函数
return {}
},
methods: {}, // 组件自定义事件
watch: {}, // 侦听属性
computed: {}, // 计算属性
mounted () {} // 生命周期钩子函数 - 都可以用
})
*/
// 组件名称在定义的时候可以写成
// Vue.component('component-name', {}) 连接符
// Vue.component('componentName', {}) 驼峰式命名
// 使用 <component-name></component-name>
// 1.定义组件
const MyButton = {
template: `<div>
<h3>vue的全局注册组件</h3>
{{ msg }}
</div>`,
data () {
return {
msg: '组件的初始化数据展示'
}
}
}
// 2.全局注册组件
// Vue.component('my-button', MyButton);
Vue.component('myButton', MyButton);
const app = new Vue({
el: '#app'
})
</script>
局部注册组件
<body>
<div id="app">
<my-button ></my-button>
</div>
</body>
<script>
// 局部注册组件
// 在vue实例或者组件中添加 components 选项,在其内部进行组件的注册
/**
components: {
'component-name': {
template: '',
data () { return {}}
...
}
}
* */
// 1.定义组件
const MyButton = {
template: `<div>
<h3>vue的局部注册组件</h3>
{{ msg }}
</div>`,
data () {
return {
msg: '组件的初始化数据展示'
}
}
}
const app = new Vue({
el: '#app',
components: {
'my-button': MyButton
}
})
</script>
异步请求数据
<body>
<div id="app">
<my-button ></my-button>
</div>
</body>
<script>
// 局部注册组件
// 在vue实例或者组件中添加 components 选项,在其内部进行组件的注册
/**
components: {
'component-name': {
template: '',
data () { return {}}
...
}
}
* */
// 1.定义组件
const MyButton = {
template: `<div>
<h3>vue的局部注册组件</h3>
{{ msg }}
<ul>
<li v-for="item of list" :key="item.proid">{{item.proname}}</li>
</ul>
</div>`,
data () {
return {
msg: '组件的初始化数据展示',
list: []
}
},
mounted () {
fetch('http://daxun.kuboy.top/api/pro').then(res => res.json()).then(data => {
console.log(data)
this.list = data.data
})
}
}
const app = new Vue({
el: '#app',
components: {
'my-button': MyButton
}
})
</script>
抽离组件的模板
- 1.使用script标签 — 老式写法
<script type="text/x-template" id="button"></script>
- 2.使用template标签 — 推荐
<template id="button"></template>
组件之间的传值
父子组件
<body>
<div id="app">
<!-- 不要直接使用content这种关键词<content></content> -->
<my-content></my-content>
</div>
</body>
<template id='content'>
<div>
<h3>content 父级</h3>
<my-nav></my-nav>
</div>
</template>
<template id='nav'>
<div>
<h3>nav 子级</h3>
</div>
</template>
<script>
const Nav = {
template : '#nav'
}
// 注意 Content 下方 引用 Nav 时 需要先在上方定义,否则报错
const Content = {
template : '#content' ,
components: {
'my-nav' : Nav ,
}
}
new Vue({
el : '#app' ,
components: {
'my-content' : Content ,
}
})
</script>
父组件给子组件传值
- 父组件中的数据自己并不渲染,通过某一种方式传递给子组件,子组件负责渲染
- 这种方式就是组件的属性传值方式
- 父组件(Content)在调用子组件(Nav)的地方,给他添加一个自定义的属性(data), 那么自定义属性的值就是父组件需要传递给子组件的值,如果属性的值是一个变量,就需要使用绑定属性 (如果属性的值是boolean或者number类型,也需要使用绑定属性)
<my-nav :data="list" :flag="true" :num="10"></my-nav>
- 在子组件Nav定义的地方,添加一个选项props,props就是用来描述接收父组件的地方
- props选项有三种写法
- 1.props的形式为数组,数组的元素就是 自定义的属性名
props: ['data', 'flag', 'num']
- 可以直接在子组件的模板中通过props数组元素访问数据
<body>
<div id="app">
<my-content></my-content>
</div>
</body>
<template id="nav">
<div>
这里就是nav - {{ flag }} - {{ num }}
<ul>
<!-- 这里用的时data不是list -->
<li v-for="(item, index) of data" :key="index">
{{ item }}
</li>
</ul>
</div>
</template>
<template id="content">
<div>
这里就是content区域
<my-nav :data="list" :flag="true" :num="10"></my-nav>
</div>
</template>
<script>
// 父组件中的数据自己并不渲染,通过某一种方式传递给子组件,子组件负责渲染
// 这种方式就是组件的属性传值方式
// 父组件(Content)在调用子组件(Nav)的地方,给他添加一个自定义的属性(data), 那么自定义属性的值就是父组件需要传递给子组件的值,如果属性的值是一个变量,就需要使用绑定属性(如果属性的值是boolean或者number类型,也需要使用绑定属性)
// <my-nav :data="list" :flag="true" :num="10"></my-nav>
// 在子组件Nav定义的地方,添加一个选项props,props就是用来描述接收父组件的地方
// props选项有三种写法
// 1.props的形式为数组,数组的元素就是 自定义的属性名
// props: ['data', 'flag', 'num']
// 可以直接在子组件的模板中通过props数组元素访问数据
const Nav = { // 子组件
props: ['data', 'flag', 'num'],
template: '#nav'
}
const Content = { // 父组件
template: '#content',
data () {
return {
list: ['a', 'b', 'c', 'd']
}
},
components: {
'my-nav': Nav
}
}
new Vue({
el: '#app',
components: {
'my-content': Content
}
})
</script>
- 2 props的形式为对象,key值为自定义的属性名,value值为数据类型 — 严谨
props: { data: Array }
- 可以直接在子组件的模板中通过props对象的key值访问数据
<body>
<div id="app">
<my-content></my-content>
</div>
</body>
<template id="nav">
<div>
这里就是nav区域 - {{ num }} - {{ flag }}
<ul>
<li v-for="item of data" :key='item.proid'>
{{ item.proname }}
</li>
</ul>
</div>
</template>
<template id="content">
<div>
这里就是content区域
<my-nav :data="list" :flag="true" :num="1"></my-nav>
</div>
</template>
<script>
// 父组件中的数据自己并不渲染,通过某一种方式传递给子组件,子组件负责渲染
// 这种方式就是组件的属性传值方式
// 父组件(Content)在调用子组件(Nav)的地方,给他添加一个自定义的属性(data), 那么自定义属性的值就是父组件需要传递给子组件的值,如果属性的值是一个变量,就需要使用绑定属性(如果属性的值是boolean或者number类型,也需要使用绑定属性)
// <my-nav :data="list" :flag="true" :num="10"></my-nav>
// 在子组件Nav定义的地方,添加一个选项props,props就是用来描述接收父组件的地方
// props选项有三种写法
// 1.props的形式为数组,数组的元素就是 自定义的属性名
// props: ['data', 'flag', 'num']
// 2. props的形式为对象,key值为自定义的属性名,value值为数据类型 --- 严谨
// props: { data: Array }
// 可以直接在子组件的模板中通过props数组元素访问数据
const Nav = {
// props: ['data'],
props: {
data: Array,
num: Number,
flag: Boolean
},
template: '#nav'
}
const Content = {
template: '#content',
data () {
return {
list: []
}
},
mounted () {
fetch('http://localhost:3000/api/pro').then(res => res.json()).then(data => {
console.log(data)
this.list = data.data
})
},
components: {
'my-nav': Nav
}
}
new Vue({
el: '#app',
components: {
'my-content': Content
}
})
</script>
- 3.props的形式为对象,key值为自定义的属性名,value值为一个对象,在此对象下,第一个key为type,value值为数据类型,第二个key值为 default,value值为默认值,如果默认的数据是数组或者对象,需要使用函数,返回默认值 — 更加严谨
props: { data: { type: Array, defualt: () => { return []}, num: { type: Number, default: 000}, flag: { type: Boolean, default: false}}
- 可以直接在子组件的模板中通过props对象的key值访问数据
<body>
<div id="app">
<my-content></my-content>
</div>
</body>
<template id="nav">
<div>
这里就是nav区域 - {{ num }} - {{ flag }}
<ul>
<li v-for="item of data" :key='item.proid'>
{{ item.proname }}
</li>
</ul>
</div>
</template>
<template id="content">
<div>
这里就是content区域
<my-nav :data="list" :flag="true" :num="1"></my-nav>
<my-nav></my-nav>
</div>
</template>
<script>
// 父组件中的数据自己并不渲染,通过某一种方式传递给子组件,子组件负责渲染
// 这种方式就是组件的属性传值方式
// 父组件(Content)在调用子组件(Nav)的地方,给他添加一个自定义的属性(data), 那么自定义属性的值就是父组件需要传递给子组件的值,如果属性的值是一个变量,就需要使用绑定属性(如果属性的值是boolean或者number类型,也需要使用绑定属性)
// <my-nav :data="list" :flag="true" :num="10"></my-nav>
// 在子组件(Nav)定义的地方,添加一个选项props,props就是用来描述接收父组件数据的地方
// props选项有三种写法
// 1.props的形式为数组,数组的元素就是 自定义的属性名
// props: ['data', 'flag', 'num']
// 可以直接在子组件的模板中通过props数组元素访问数据
// 2. props的形式为对象,key值为自定义的属性名,value值为数据类型 --- 严谨
// props: { data: Array }
// 可以直接在子组件的模板中通过props对象的key值访问数据
// 3.props的形式为对象,key值为自定义的属性名,value值为一个对象,在此对象下,第一个key为type,value值为数据类型,第二个key值为 default,value值为默认值,如果默认的数据是数组或者对象,需要使用函数,返回默认值 --- 更加严谨
// props: { data: { type: Array, defualt: () => { return []}, num: { type: Number, default: 000}, flag: { type: Boolean, default: false}}
// 可以直接在子组件的模板中通过props对象的key值访问数据
const Nav = {
// props: ['data'],
// props: {
// data: Array,
// num: Number,
// flag: Boolean
// },
props: {
data: {
type: Array,
default: () => { // 如果是数组或者对象,使用函数返回
return [
{
proid: '111',
proname: '默认值'
}
]
}
},
num: {
type: Number,
default: 100
},
flag: {
type: Boolean,
default: false
}
},
template: '#nav'
}
const Content = {
template: '#content',
data () {
return {
list: []
}
},
mounted () {
fetch('http://localhost:3000/api/pro').then(res => res.json()).then(data => {
console.log(data)
this.list = data.data
})
},
components: {
'my-nav': Nav
}
}
new Vue({
el: '#app',
components: {
'my-content': Content
}
})
</script>
子组件给父组件传值
- 在子组件中,通过某一个事件触发,执行以下事件
- this.$emit(自定义事件名, 传递的参数)
- 在父组件调用子组件的地方,绑定 自定义事件名 的事件,此事件由 父组件实现,注意绑定事件不加(),方法实现有默认参数,此参数的值就是子组件传递过来的值
<body>
<div id="app">
<my-content></my-content>
</div>
</body>
<template id="nav">
<div>
这里就是nav区域
<button @click="senddata('aaa')">改变为aaa</button>
<button @click="senddata('bbb')">改变为bbb</button>
<button @click="senddata('ccc')">改变为ccc</button>
<button @click="senddata('ddd')">改变为ddd</button>
</div>
</template>
<template id="content">
<div>
这里就是content区域 - {{ tip }}
<my-nav @my-event="getData"></my-nav>
</div>
</template>
<script>
// 在子组件中,通过某一个事件触发,执行以下事件
// this.$emit(自定义事件名, 传递的参数)
// 在父组件调用子组件的地方,绑定 自定义事件名 的事件,此事件由 父组件实现,注意绑定事件不加(),方法实现有默认参数,此参数的值就是子组件传递过来的值
const Nav = {
template: '#nav',
methods: {
senddata (val) {
this.$emit('my-event', val)
}
}
}
const Content = {
template: '#content',
data () {
return {
tip: '1'
}
},
methods: {
getData (val) {
this.tip = val
}
},
components: {
'my-nav': Nav
}
}
new Vue({
el: '#app',
components: {
'my-content': Content
}
})
</script>
非父子组件之间传值
// 非父子组件之间传值 — 中央事件总线
// new Vue实例作为中央事件总监 const bus = new Vue()
// 在需要传递数据的一方,通过以下代码传递数据
// bus.
on(自定义事件名, function (val) {})
<body>
<div id="app">
<my-nav></my-nav>
<my-content></my-content>
</div>
</body>
<template id="nav">
<div>
<button @click="sendData('首页')">首页</button>
<button @click="sendData('分类')">分类</button>
<button @click="sendData('购物车')">购物车</button>
<button @click="sendData('我的')">我的</button>
</div>
</template>
<template id="content">
<div>
content - {{ tip }}
</div>
</template>
<script>
// 非父子组件之间传值 --- 中央事件总线
// new Vue实例作为中央事件总监 const bus = new Vue()
// 在需要传递数据的一方,通过以下代码传递数据
// bus.$emit(自定义事件名, 传递的数据)
// 在需要接受数据的一方,通过以下代码接收数据
// bus.$on(自定义事件名, function (val) {})
const bus = new Vue()
const Nav = {
template: '#nav',
methods: {
sendData (val) {
bus.$emit('my-event', val)
}
}
}
const Content = {
template: '#content',
data () {
return {
tip: '首页'
}
},
mounted () {
// const that = this
// bus.$on('my-event', function (val) {
// console.log(val)
// that.tip = val
// })
bus.$on('my-event', (val) => {
console.log(val)
this.tip = val
})
}
}
new Vue({
el: '#app',
components: {
'my-nav': Nav,
'my-content': Content
}
})
</script>
动态组件
<body>
<div id="app">
<button @click="com = 'home'">首页</button>
<button @click="com = 'kind'">分类</button>
<button @click="com = 'cart'">购物车</button>
<button @click="com = 'user'">我的</button>
<component :is="com"></component>
</div>
</body>
<template id="home">
<div>首页</div>
</template>
<template id="kind">
<div>分类</div>
</template>
<template id="cart">
<div>购物车</div>
</template>
<template id="user">
<div>我的</div>
</template>
<script>
const Home = {
template: '#home'
}
const Kind = {
template: '#kind'
}
const Cart = {
template: '#cart'
}
const User = {
template: '#user'
}
new Vue({
el: '#app',
data: {
com: 'home'
},
components: {
'home': Home,
'kind': Kind,
'cart': Cart,
'user': User
}
})
</script>
如果每个中都含有一个表单,那么再来分析情况,输入之后切换组件,输入框中的值就全部清空 ---- 切换组件 实际上就是触发了组件的 销毁 与创建
<body>
<div id="app">
<button @click="com = 'home'">首页</button>
<button @click="com = 'kind'">分类</button>
<button @click="com = 'cart'">购物车</button>
<button @click="com = 'user'">我的</button>
<component :is="com"></component>
</div>
</body>
<template id="home">
<div>首页
<input type="text">
</div>
</template>
<template id="kind">
<div>分类
<input type="text">
</div>
</template>
<template id="cart">
<div>购物车
<input type="text">
</div>
</template>
<template id="user">
<div>我的
<input type="text">
</div>
</template>
<script>
const Home = {
template: '#home',
destroyed () {
console.log('home destroyed')
}
}
const Kind = {
template: '#kind',
destroyed () {
console.log('kind destroyed')
}
}
const Cart = {
template: '#cart',
destroyed () {
console.log('cart destroyed')
}
}
const User = {
template: '#user',
destroyed () {
console.log('user destroyed')
}
}
new Vue({
el: '#app',
data: {
com: 'home'
},
components: {
'home': Home,
'kind': Kind,
'cart': Cart,
'user': User
}
})
</script>
看需求,有的不需要销毁与创建,有的正好
可以使用 keep-alive 缓存 当前的组件,避免组件的销毁与重建
<keep-alive><component :is="com"></component></keep-alive>
实质上触发了组件的 activated 和 deactivated 生命周期钩子 - 10个生命周期的钩子函数
<body>
<div id="app">
<button @click="com = 'home'">首页</button>
<button @click="com = 'kind'">分类</button>
<button @click="com = 'cart'">购物车</button>
<button @click="com = 'user'">我的</button>
<keep-alive>
<component :is="com"></component>
</keep-alive>
</div>
</body>
<template id="home">
<div>首页
<input type="text">
</div>
</template>
<template id="kind">
<div>分类
<input type="text">
</div>
</template>
<template id="cart">
<div>购物车
<input type="text">
</div>
</template>
<template id="user">
<div>我的
<input type="text">
</div>
</template>
<script>
const Home = {
template: '#home',
activated () {
console.log('home activated')
},
deactivated () {
console.log('home deactivated')
},
destroyed () {
console.log('home destroyed')
}
}
const Kind = {
template: '#kind',
activated () {
console.log('kind activated')
},
deactivated () {
console.log('kind deactivated')
},
destroyed () {
console.log('kind destroyed')
}
}
const Cart = {
template: '#cart',
activated () {
console.log('cart activated')
},
deactivated () {
console.log('cart deactivated')
},
destroyed () {
console.log('cart destroyed')
}
}
const User = {
template: '#user',
activated () {
console.log('user activated')
},
deactivated () {
console.log('user deactivated')
},
destroyed () {
console.log('user destroyed')
}
}
new Vue({
el: '#app',
data: {
com: 'home'
},
components: {
'home': Home,
'kind': Kind,
'cart': Cart,
'user': User
}
})
</script>
思考: 要不就是创建和销毁,要不就是缓存与显示,那么需求如果是有些需要销毁和创建,有的需要缓存呢?
<!-- 这里的include 一定不要有空格 -->
<keep-alive include="homecom,kindcom">
<component :is="com"></component>
</keep-alive>
homecom,kindcom 是定义组件的时候给每一个组件起的name,注意include中间不要加空格
<body>
<div id="app">
<button @click="com = 'home'">首页</button>
<button @click="com = 'kind'">分类</button>
<button @click="com = 'cart'">购物车</button>
<button @click="com = 'user'">我的</button>
<keep-alive include="homecom,kindcom">
<component :is="com"></component>
</keep-alive>
</div>
</body>
<template id="home">
<div>首页
<input type="text">
</div>
</template>
<template id="kind">
<div>分类
<input type="text">
</div>
</template>
<template id="cart">
<div>购物车
<input type="text">
</div>
</template>
<template id="user">
<div>我的
<input type="text">
</div>
</template>
<script>
const Home = {
name: 'homecom',
template: '#home',
activated () {
console.log('home activated')
},
deactivated () {
console.log('home deactivated')
},
destroyed () {
console.log('home destroyed')
}
}
const Kind = {
name: 'kindcom',
template: '#kind',
activated () {
console.log('kind activated')
},
deactivated () {
console.log('kind deactivated')
},
destroyed () {
console.log('kind destroyed')
}
}
const Cart = {
name: 'cartname',
template: '#cart',
activated () {
console.log('cart activated')
},
deactivated () {
console.log('cart deactivated')
},
destroyed () {
console.log('cart destroyed')
}
}
const User = {
name: 'usercom',
template: '#user',
activated () {
console.log('user activated')
},
deactivated () {
console.log('user deactivated')
},
destroyed () {
console.log('user destroyed')
}
}
new Vue({
el: '#app',
data: {
com: 'home'
},
components: {
'home': Home,
'kind': Kind,
'cart': Cart,
'user': User
}
})
</script>
脚手架
创建项目
准备工作
node -v
npm -v
安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装脚手架 3版本以上 — 强烈推荐
cnpm i @vue/cli -g
vue -v
方式1 - 命令行操作
vue create myapp
方式2 - 可视化操作
vue ui
安装脚手架 2版本 ----- 不推荐
cnpm i vue-cli -g
创建项目
vue init webpack myapp
如果你安装的是3的脚手架,但是你又需要使用2的创建项目
cnpm i @vue/cli-init -g
vue init webpack myapp