Vue全家桶 | 地址 |
---|---|
Vue全家桶之Vue基础指令(一) | https://blog.csdn.net/Augenstern_QXL/article/details/120117044 |
Vue全家桶之Vue组件化开发(二) | https://blog.csdn.net/Augenstern_QXL/article/details/120117322 |
Vue全家桶之VueCLI 脚手架V2→V4版本(三) | https://blog.csdn.net/Augenstern_QXL/article/details/120117453 |
Vue全家桶之webpack详解(四) | https://blog.csdn.net/Augenstern_QXL/article/details/120297794 |
Vue全家桶之Vue-router路由(五) | https://blog.csdn.net/Augenstern_QXL/article/details/120339146 |
Vue全家桶之VueX(六) | https://blog.csdn.net/Augenstern_QXL/article/details/120339600 |
如果将一整个应用的所有处理逻辑都放在一起,处理起来就会变得非常复杂,而且不利于后续的迭代和扩展。如果将一整个应用拆分成一个个页面,一个个页面内部又拆分成一个个独立的组件,每个组件完成属于自己内部独立的功能,组件汇集成页面,页面汇集成应用。那么整个应用的管理和维护则变得非常清晰。
我们来看下面的代码:
<body>
<div>
<header>头部header>
<header>身体header>
<header>尾部header>
div>
<div id="app">
div>
<script src="../js/vue.js">script>
<script>
// 1.创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好Vue3!'
}
}
}).mount('#app');
script>
body>
如果我们要复用上述代码,那么我们只能复制 div 里面的代码,然后达到复用的效果,这样就太麻烦了,所以组件化开发就可以解决此类问题。
Vue.component()
方法注册组件<body>
<div id="app">
<button-counter>button-counter>
<button-counter>button-counter>
<button-counter>button-counter>
div>
<script src="../js/vue.js">script>
<script>
// 1.创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
}
});
// 2.定义一个组件(全局组件)
app.component('button-counter',{
data() {
return {
count: 0
}
},
template: `
`
})
// 3. 挂载vue实例
app.mount('#app');
script>
body>
引用短横线式命名的组件,调用时
app.component('my-component-name',{
data(){
return {
}
},
template: ``
})
引用驼峰式命名的组件,调用时
和
都可以
但是我们直接在DOM(即非字符串的模板)中使用时只有短横线法是有效的。在后面CLI调用两种方法都可以
app.component('My-Component-Name',{
data(){
return {
}
},
template: ``
})
全局组件:在整个Vue实例中都可以被调用
局部组件:只能在当前组件中被使用
如下代码,我们在 app 下注册了一个全局组件,这意味着该组件可以在app实例内部任意地方使用:我们可以在 app 实例下使用,也可以在 home 实例下使用,也可以在 message 实例下使用。
<body>
<div id="app">
<div id="home">
<span>首页span>
<button-counter>button-counter>
div>
<div id="message">
<span>消息span>
<button-counter>button-counter>
div>
<button-counter>button-counter>
div>
<script src="../js/vue.js">script>
<script>
// 1.创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
}
});
// 2.定义一个组件(全局组件)
app.component('button-counter',{
data() {
return {
count: 0
}
},
template: `
`
})
// 3. 挂载vue实例
app.mount('#app');
script>
body>
当然我们也可以设置多个全局组件,代码如下:
<body>
<div id="app">
<div id="home">
<span>首页span>
<button-counter>button-counter>
div>
<div id="message">
<span>消息span>
<button-counter>button-counter>
<lk-box>lk-box>
div>
<button-counter>button-counter>
div>
<script src="../js/vue.js">script>
<script>
// 1.创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
}
});
// 2.定义一个组件(全局组件)
app.component('button-counter',{
data() {
return {
count: 0
}
},
template: `
`
})
// 定义第二个全局组件
app.component('lk-box',{
template: `
盒子组件
`
})
// 3. 挂载vue实例
app.mount('#app');
script>
body>
当然我们全局组件之间可以相互使用,使用方式如下:我们在定义第二个全局组件,若向使用第一个全局组件,只需要将第一个全局组件的名称标签写入模板template中即可
// 定义第二个全局组件
app.component('lk-box',{
template: `
盒子组件
`
})
如下代码,局部组件是使用一个常量来接收,我们将此局部组件通过 components
可以将其挂载在app实例中,这样我们在 app 实例里面就可以使用了
<body>
<div id="app">
<lk-count>lk-count>
div>
<script src="../js/vue.js">script>
<script>
// 注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
template: `
`
}
// 创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
},
// 组件选项
components: {
'lk-count': Counter
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
当然我们可以定义多个局部组件,然后通过 components
可以将其挂载在app实例中:
<body>
<div id="app">
<lk-count>lk-count>
<cc-count>cc-count>
div>
<script src="../js/vue.js">script>
<script>
// 注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
template: `
`
}
// 注册第二个局部组件
const Box = {
template: `
盒子组件
`
}
// 创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
},
// 组件选项
components: {
'lk-count': Counter,
'cc-count': Box
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
若我们想要局部组件之间相互使用,不能像全局组件那样在注册时向模板template写入名称标签,比如在注册组件时通过components
将其挂载在想使用的实例中,然后再如全局组件那样向模板template写入名称标签:
// 注册第二个局部组件
const Box = {
components: {
'lk-count': Counter,
},
template: `
盒子组件
`
}
全局组件:在整个Vue实例中都可以被调用,若想要全局组件之间相互使用,只需将想使用全局组件的名称写入 template
中
局部组件:只能在当前组件中被使用,若想在其他组件中使用,必须使用 components
将其挂载在想使用的组件中,然后再如全局组件那样向模板template写入名称标签
我们之后其实对局部组件用的更多一些。
template
模块写法不够清晰,如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰
Vue 提供了两种方案来定义HTML模板内容
下面代码是我们之前注册局部组件的代码,我们可以看到 templates
里面有一个 button 按钮
<body>
<div id="app">
<lk-count>lk-count>
div>
<script src="../js/vue.js">script>
<script>
// 注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
template: `
`
}
// 创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
},
// 组件选项
components: {
'lk-count': Counter,
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
我们使用 script 标签将其抽离
<body>
<div id="app">
<lk-count>lk-count>
div>
<script type="text/x-template" id="mycount">
<button @click="count++">你点击了{{count}}次</button>
script>
<script src="../js/vue.js">script>
<script>
// 注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
// 使用#id
template: '#mycount'
}
// 创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
},
// 组件选项
components: {
'lk-count': Counter,
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
我们使用 template 标签将其抽离
<body>
<div id="app">
<lk-count>lk-count>
div>
<template id="mycount">
<button @click="count++">你点击了{{count}}次button>
template>
<script src="../js/vue.js">script>
<script>
// 注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
template: '#mycount'
}
// 创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
},
// 组件选项
components: {
'lk-count': Counter,
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
问题:组件可以访问Vue实例数据吗?
结论:组件不能直接访问Vue实例中的 data
组件是一个单独功能模块的封装:
组件自己的数据存放在哪呢?
组件对象也有一个 data 属性(也可以有 methods 属性)
只是这个 data 属性必须是一个函数
而且这个函数返回一个对象,对象内部保存着数据
// 注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
template: `
`,
methods: {}
}
为什么 data 在组件中必须是一个函数呢?
我这里来写一个简单介绍:我们设立两个对象,虽然都是空对象,但是两者不相等。
let obj1 = {};
let obj2 = {};
console.log(obj1 === obj2); // false
上述代码在内存中的示意图如下:
我们创建一个对象
let obj = {
name: '秦晓'
}
let p1 = obj;
let p2 = obj;
let p3 = obj;
console.log(p1,p2,p3);
console.log(p1 === p2); // true
let obj = {
name: '秦晓'
}
let p1 = obj;
let p2 = obj;
let p3 = obj;
// 我们修改p1的name
p1.name = '大林';
console.log(p1,p2,p3);
console.log(p1 === p2);
从上述例子中就可以看出,如果我们在使用对象的时候,很容易造成值拷贝。现在我们来回答一下为什么 data 在组件中必须是一个函数呢?
组件通信的常用方式有4种:
在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的
但是,在开发中,往往一些数据确实需要从上层传递到下层
组件中,使用选项 props
来声明需要从父级接收到的数据(properties)
props
的值有两种方式:
我们先来看方式一:先看如下代码,我们使用局部组件展示数据
<body>
<div id="app">
<lk-box>lk-box>
div>
<template id="box">
<div>
<h1>技能掌握h1>
<li>Webli>
<li>Pythonli>
<li>Javali>
div>
template>
<script src="../js/vue.js">script>
<script>
// 注册一个局部组件
const Box = {
template: '#box'
};
// 创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
}
},
components: {
'lk-box': Box
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
如果我们的数据是从服务器端返回的,那么我们就需要动态绑定数据,代码如下:
<body>
<div id="app">
<lk-box :brand="msg1" :colleges="msg2">lk-box>
div>
<template id="box">
<div>
<h1>{{brand}}h1>
<ul>
<li v-for="item in colleges">{{item}}li>
ul>
div>
template>
<script src="../js/vue.js">script>
<script>
// 1.注册一个局部组件(子组件)
const Box = {
// 子组件接收数据
props: ['brand','colleges'],
template: '#box'
};
// 2.将子组件在父组件里面注册
const app = Vue.createApp({
data(){
return {
msg1: '技能掌握',
msg2: ['Web','Python','Java']
}
},
components: {
'lk-box': Box
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
props
选项是使用一个数组props
进行类型等验证时,就需要对象写法了我们可以在 props 里面限制父组件给子组件传递的数据类型
<body>
<div id="app">
<cpn :cmessage="message" :cmovies="movies">cpn>
div>
<template id="cpn">
<div>
<h1>{{cmovies}}}h1>
<h1>{{cmessage}}h1>
div>
template>
<script src="../js/vue.js">script>
<script>
// 父传子: props
const cpn = {
template: '#cpn',
props: {
// 1.类型限制
cmovies: Array, // 限制父组件传的是数组类型
cmessage: String, // 限制父组件传的是字符串类型
}
}
// root组件,我们当作父组件
const app = Vue.createApp({
data(){
return {
message: '你好啊',
movies: ['海王', '海贼王', '海尔兄弟']
}
},
components: {
//对象字面量增强写法的属性增强写法
cpn
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
类型一般支持:String、Number、Boolean、Array、Object、Date、Function、Symbol、自定义类型
type
: 限制的类型default
: 如果没有传值,给一个默认值
required
: 必须的,即意味着这个值是必须要传递的,不传就报错<body>
<div id="app">
<cpn :cmessage="message" :cmovies="movies">cpn>
div>
<template id="cpn">
<div>
<h1>{{cmovies}}}h1>
<h1>{{cmessage}}h1>
div>
template>
<script src="../js/vue.js">script>
<script>
// 父传子: props
const cpn = {
template: '#cpn',
props: {
// 2.提供一些默认值, 以及必传值
cmessage: {
type: String, // 类型限制为 String
default: 'aaaaaaaa', // 如果没有传值,则给一个默认值
required: true // required 必须的,即意味着这个值是必须要传递的,不传就报错
},
// 类型是对象或者数组时, 默认值必须是一个函数
cmovies: {
type: Array,
default() {
return []
}
}
}
}
// root组件,我们当作父组件
const app = Vue.createApp({
data(){
return {
message: '你好啊',
movies: ['海王', '海贼王', '海尔兄弟']
}
},
components: {
//对象字面量增强写法的属性增强写法
cpn
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
当我们 props 里面的属性是驼峰写法的时,在传入值时需要进行 -
连接
<div id="app">
<cpn :c-info="info" :child-my-message="message" >cpn>
div>
<template id="cpn">
<div>
<h2>{{cInfo}}h2>
<h2>{{childMyMessage}}h2>
div>
template>
<script src="../js/vue.js">script>
<script>
const cpn = {
template: '#cpn',
props: {
// 驼峰写法cInfo
cInfo: {
//类型是对象或者数组时, 默认值必须是一个函数
type: Object,
default() {
return {}
}
},
childMyMessage: {
type: String,
default: ''
}
}
}
const app = new Vue({
el: '#app',
data: {
info: {
name: 'why',
age: 18,
height: 1.88
},
message: 'aaaaaa'
},
components: {
cpn
}
})
script>
props
用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件去。这个时候,我们需要使用自定义事件来完成
自定义事件的流程:
$emit()
来发射事件v-on
来监听子组件事件我们来看下方代码,我们在子组件中放置一个按钮,在父组件中对子组件进行注册:
<body>
<div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;">
<lk-box>lk-box>
div>
<template id="box">
<div style="background-color:red;width: 200px;height: 200px;">
<button @click="btnClick">点我button>
div>
template>
<script src="../js/vue.js">script>
<script>
// 子组件
const Box = {
template: '#box',
methods: {
btnClick(){
alert('点击了');
}
},
};
const app = Vue.createApp({
data(){
return {
msg: '你好Vue3!'
}
},
components: {
'lk-box': Box
},
});
// 挂载vue实例
app.mount('#app');
script>
body>
如果我们点击了子组件,希望让父组件知道,那么子组件就要发射事件到父组件:例如如下代码,我们使用子组件发射事件来触发父组件的 boxFunc 函数:
<body>
<div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;">
<lk-box @box-click="boxFunc">lk-box>
div>
<template id="box">
<div style="background-color:red;width: 200px;height: 200px;">
<button @click="btnClick">点我button>
div>
template>
<script src="../js/vue.js">script>
<script>
// 子组件
const Box = {
template: '#box',
methods: {
btnClick(){
alert('点击了');
// 发射事件
// 第一个参数是自定义事件的名称,第二个参数是自定义事件的参数
this.$emit('box-click');
}
},
};
// 父组件
const app = Vue.createApp({
data(){
return {
msg: '你好Vue3!'
}
},
components: {
'lk-box': Box
},
methods: {
boxFunc(){
console.log('子组件中的按钮发生了点击')
}
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
那如果我们点击子组件按钮,同时还想要传递参数给父组件,代码如下:
$emit()
事件传递参数<body>
<div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;">
<lk-box @box-click="boxFunc">lk-box>
div>
<template id="box">
<div style="background-color:red;width: 200px;height: 200px;">
<button @click="btnClick">点我button>
div>
template>
<script src="../js/vue.js">script>
<script>
// 子组件
const Box = {
template: '#box',
methods: {
btnClick(){
alert('点击了');
// 发射事件
// 第一个参数是自定义事件的名称,第二个参数是自定义事件的参数
this.$emit('boxClick','秦晓');
}
},
};
// 父组件
const app = Vue.createApp({
data(){
return {
msg: '你好Vue3!'
}
},
components: {
'lk-box': Box
},
methods: {
boxFunc(item){
console.log('子组件中的按钮发生了点击');
console.log(item);
}
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
如果我们想传递多个参数,我们可以以一个对象或者数组的形式传递,例如我们传递一个对象:
<body>
<div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;">
<lk-box @box-click="boxFunc">lk-box>
div>
<template id="box">
<div style="background-color:red;width: 200px;height: 200px;">
<button @click="btnClick">点我button>
div>
template>
<script src="../js/vue.js">script>
<script>
// 子组件
const Box = {
template: '#box',
methods: {
btnClick(){
alert('点击了');
const dataObj = {
name: '秦晓',
age: 20
}
// 发射事件
this.$emit('boxClick',dataObj);
}
},
};
// 父组件
const app = Vue.createApp({
data(){
return {
msg: '你好Vue3!'
}
},
components: {
'lk-box': Box
},
methods: {
boxFunc(item){
console.log('子组件中的按钮发生了点击');
console.log(item);
}
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。
$children(Vue3.x已经废弃)
或 $refs
$parent
$refs
的使用
$refs
和 ref 指令通常是一起使用的this.$refs.ID
就可以访问到该组件了
this.$refs.ID.xx
就可以拿到该组件里面的属性数据了例如下方代码,我们给子组件使用 ref="box1"
绑定ID,在父组件里面使用 this.$refs.box1
就可以拿到该组件,接着使用this.$refs.box1.msg
拿到该组件的 msg
属性数据
<body>
<div id="app">
<lk-box ref="box1">lk-box>
<button @click="getChildComponent">获取子组件button>
div>
<template id="box">
<div style="background-color:red;width: 200px;height: 200px;">
<button @click="btnClick">点我button>
div>
template>
<script src="../js/vue.js">script>
<script>
// 定义局部组件
const Box = {
data(){
return {
msg: '春风十里'
}
},
methods: {
btnClick(){
alert('点击了按钮')
}
},
template: '#box'
};
// 父组件
const app = Vue.createApp({
data(){
return {
msg: '你好Vue3!'
}
},
components: {
'lk-box': Box
},
methods: {
// 获取子组件
getChildComponent(){
// this.$refs.box1 相当于拿到了子组件
// this.$refs.box1.msg 就是拿到了子组件里面的 msg 数据
// this.$refs.box1.btnClick 就是拿到了子组件里面的 btnClick 方法
console.log(this.$refs.box1.msg);
}
}
});
// 挂载vue实例
app.mount('#app');
script>
body>
如果我们想在子组件中直接访问父组件,可以通过 $parent
,如果想访问根组件,可以通过 $root
尽管在 Vue 开发中,我们允许通过 $parent
来访问父组件,但是在真实开发中尽量不要这要做
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了
因为子组件一般会复用,如果我们将子组件放入另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题
例如如下代码,我们做了三层嵌套,最外层是根组件Vue实例,第二层是div盒子,最里面一层是 button 按钮。我们现在想点击 最里面的 button 来拿到div父组件或者root根组件。
<body>
<div id="app">
<lk-box>lk-box>
div>
<template id="box1">
<button @click="btnClick">点击了按钮{{count}}次button>
template>
<template id="box2">
<div style="background-color:red; width: 200px; height: 200px">
<lk-button>lk-button>
div>
template>
<script src="../js/vue.js">script>
<script>
// 定义局部组件1
const LKButton = {
data(){
return {
count: 0
}
},
template: '#box1',
methods: {
btnClick(){
// 子组件访问父组件
// this.$parent 可以拿到该组件的父组件,也就是button子组件的父组件div
// console.log(this.$parent);
this.count++;
console.log(this.$parent.count);
// 子组件访问根组件
// this.$root 可以拿到根组件,也就是vue实例
// console.log(this.$root);
console.log(this.$root.msg);
}
}
};
// 定义局部组件2
const LKBox = {
data(){
return {
count: 0
}
},
template: '#box2',
components: {
'lk-button': LKButton
}
};
// 父组件
const app = Vue.createApp({
data(){
return {
msg: '你好Vue3!'
}
},
components: {
'lk-box': LKBox
},
});
// 挂载vue实例
app.mount('#app');
script>
body>