vue组件有两种:脚本化组件,单文件组件

脚本化组件的缺点:
1.htm代码是作为js的字符串进行编写,所以组装和开发的时候不易理解,而且没有高亮效果
2.普通组件用在小项目中非常合适,但是复杂的大项目中,如果把更多的组件放在html文件中,那么维护成本就会变得非常
昂贵。
3.脚本化组件只是整合了js和html,但是css代码被剥离出去了。使用的时候的时候不好处理,既要引入js组件又要保存css代码一起引入到页面,相当麻烦。

创建并注册组件

在组件中编辑三个标签,编写视图,vm对象和css样式

组件内容编写

template编写html代码的地方

script编写vue.js代码

style编写当前组件的样式代码

template内部有且只能有一个子标签(2.0版本的限制,3.0版本不限制),组件所有的html代码必须被包,含在这个子标签中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<button @click="num++">{{ num }}</button>
<button @click="num++">{{ num }}</button><!--vue2要求内部只能有一个子标签,但vue3可以这么写-->
</template>
<script>
const Home = {
name: 'Home',
data() {
return {
num: 0
}
}
}
export default Home;
</script>
<!--scoped表示局部样式 vue2.0如果不添加会导致样式污染其他组件 vue3也会,总之最好添加一个scoped属性-->
<style scoped>
button {
color: red;
}
</style>

在App.vue中导入、注册并调用Home组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<Home></Home>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import Home from "./components/Home"; //这里导入需要在下面组件中启用
export default {
name: 'App',
components: {
HelloWorld,
Home
}
}
</script>

<style>
##app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: ##2c3e50;
margin-top: 60px;
}
</style>

完成案例-点击加减数字

1
2
3
4
5
<template>
<button @click="num--">-</button>
<input type="text" v-model="num">
<button @click="num++">+</button>
</template>

在webstorm启动项目

其实在package.json中点击一下哪些以及设置好的命令,他会自动帮我们配置

要注意的一点就是,这个启动的包管理器就如上面所示配置信息最前面写的是Project而不是yarn,但是在后面写了yarn

在实参部分添加port和host可以更改部署的端口和IP地址

1
--port=8000 --host=0.0.0.0

然后就可以运行了

组件的嵌套

有时候开发vu项目时,页面也可以算是一个大组件,同时页面也可以分成多个子组件
因为,产生了父组件调用子组件的情况。
例如,我们就可以声明一个页面组件保存目录src/views,并提供2个页面组件,分别是Home.vue和Register..vue,作为页面组件
同时在src/components/创建一个保存子组件的目录Menu

做了几个操作:

创建Menu组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
const Menu={
name:"Menu",
data(){
return{
li_list:['主页','商城','论坛','注册'],
num:10
}
}
}
export default Menu;
</script>

<template>
<ul>
<li v-for="(member,index) in li_list" :key="index">{{member}}</li>
<button @click="num++">{{num}}</button>
<!-- <li>主页</li>-->
<!-- <li>商城</li>-->
<!-- <li>论坛</li>-->
<!-- <li>注册</li>-->
</ul>
</template>

从Register导入Menu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
import Menu from "@/components/Menu.vue";

export default {
name: "Register_page"//不能使用Register
, components: {
Menu
}
}

</script>

<template>
<h1>这是Register3页面</h1>
<Menu></Menu>
</template>

<style scoped>

</style>

从Home导入Register、Menu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<h1>Home页面</h1>
<Menu></Menu>
<p>下面是Register</p>
<Register></Register>
</template>
<script>
import Menu from "@/components/Menu.vue";
import Register from "@/View/Register.vue";
const Home = {
name: 'Home',
data() {
return {
num: 0
}
},
components:{
Menu,
Register
}
}
export default Home;
</script>

从App导入Register、Home

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<template>
<Home></Home>
<button ref="btn" class="add">提交</button>
<Register></Register>
</template>

<script>
import Home from "./View/Home"; //这里导入需要在下面组件中启用
import Register from "@/View/Register.vue";
export default {
name: 'App',
components: {
Register,
Home
},
mounted() {
console.log(this.$refs.btn)
}
}
</script>

<style>
##app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: ##2c3e50;
margin-top: 60px;
}
</style>

效果如下

Menu.vue
Register.vue
Register.vue
Menu.vue
Home.vue
App.vue
Menu.vue
index.html
main.js

组件嵌套的style影响

##Menu.vue文件中,我们对设置了button标签样式和class类选择器样式

注这里设置了局部样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
const Menu = {
name: "Menu",
}
export default Menu;
</script>
<template>
<button>子-标签选择器</button>
<button class="add">子-类选择器选择器</button>
</template>
<style scoped>
div {
border: 1px solid red;
}
button{
color: red;
}
ul {
list-style: none;
}
.add{
color: red;
}
</style>

在Home.vue中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<h1>Home页面</h1>
<button class="add">类选择器</button>
<button >标签选择器</button>
<Menu ></Menu>
</template>
<script>
import Menu from "@/components/Menu.vue";
const Home = {
name: 'Home',
data() {
return {
total: 0
}
},
components:{
Menu,
}
}
export default Home;
</script>
<style scoped>
/*.add {color: red;}*/
</style>

在App.vue中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
<Home></Home>
<button class="add">根-类选择器</button>
<button>根-标签选择器</button><!--其实写不写无所谓就跟id选择器一样-->
</template>

<script>
import Home from "./View/Home";
export default {
name: 'App',
components: {
Home
},
mounted() {
console.log(this.$refs.btn)
}
}
</script>

<style>
##app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: ##2c3e50;
margin-top: 60px;
}
</style>

就样式最内部发生了改变

去掉scoped,子组件的样式就变成了全局样式,如果你不是有意为之,那么就会造成了样式污染

组件之间传递数据

父组件的数据传递给子组件
例列如,我们希望把父组件的数据传递给子组件
可以通过props属性来进行数据传递
传递数据三个步骤:

父组件的数据传递给子组件

  1. 在父组件中,调用子组件的组名除,使用属性值的方式往下传递数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <template>
    <h1>Home页面</h1>
    <button @click="total++">Home的total:{{total}}</button>
    <!-- 父组件要发送数据给子组件,通过组件的属性传递数据,属性名就是将来的变量名,传递则需要在属性左边加上:-->
    <Menu :htotal="total" total="1100" title="来自home的数据呀!"></Menu>
    </template>
    <script>
    import Menu from "@/components/Menu.vue";
    const Home = {
    name: 'Home',
    data() {
    return {
    total: 0
    }
    },
    components:{
    Menu,
    }
    }
    export default Home;
    </script>
  2. 在子组件Menu.vue中接受上面父组件传递的数据,需要在script中,使用props属性类接受。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <script>
    const Menu = {
    name: "Menu",
    //来自父组件的数据,全部通过props来接受,2种写法:json写法:必须指定数据类型,数组写法:不用指定数据类型
    // props:{ //变量名:数据类型
    // 数据类型有
    // Number数值(整数,浮点)
    // String字符串
    // Boolean布尔值
    // Array数组
    // Object对象
    // Function函数
    // Promise异步对象
    // default默认值
    // 还可以做一些验证
    // htotal:Number,
    // total:Number,
    // title:String
    // },
    props:["htotal",'total',"title"],//数组写法可以不写类型
    }
    export default Menu;
    </script>

  3. 在子组件中的template中使用父组件传递过来的数据

    1
    2
    3
    4
    5
    <template>
    <p>来自父组件的数据 <br>变量{{htotal}}<br>数值:{{total}}<br>字符串:{{title}}</p>
    <!-- <button @click="htotal++">{{ htotal }}</button>-->
    <!-- 子组件不能直接对父组件转入的数据变量进行操作 -->
    </template>

使用父组件传递数据给子组件时,注意以下事项:

  1. 传递数据是变量,则需要在属性左边添加英文冒号。
    传递数据是变量,这种数据称之为”动态数据传递”,父组件数据改动的时候,子组件中被随之改动。
    传递数据不是变量,这种数据称之为”静态数据传递”
  2. 父组件中修改了数据,在子组件中会被同步修改。但是来自orops的数据,在子组件是不能修改的,因为不是data中声明的。在开发时这种情况,也被称为”单向数据流”

在子组件中对父组件传递的数据进行操作

1
<button @click="htotal++">{{ htotal }}</button>

单向数据流

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。

所以子组件不能直接对父组件转入的数据变量进行操作

补充:父组件传递数据到子数据的过程中,可以给子组件设置对父组件的默认值和验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// Number 类型的默认值
propD: {
type: Number,
default: 100
},
// 对象类型的默认值
propE: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propG: {
type: Function,
// 不像对象或数组的默认,这不是一个
// 工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})

子组件传递数据给父组件

  1. 在子组件中,通过his.$emit()来声明一个自定义的信号(vue中称之为自定义事件名),父组件中针对这个信号(自定义事件)在调用子组件的组件名处进行@监听。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    <script>
    const Menu = {
    name: "Menu",
    //来自父组件的数据,全部通过props来接受,2种写法:json写法:必须指定数据类型,数组写法:不用指定数据类型
    // props:{ //变量名:数据类型
    // 数据类型有
    // Number数值(整数,浮点)
    // String字符串
    // Boolean布尔值
    // Array数组
    // Object对象
    // Function函数
    // Promise异步对象
    // htotal:Number,
    // total:Number,
    // title:String
    // },
    props:["htotal",'total',"title"],//数组写法可以不写类型
    data(){
    return{
    num:0,
    message:'我是子组件里来的'
    }
    },
    methods:{
    add(){
    this.num++;
    //this,emit("自定义事件名",变量1,变量2……)
    this.$emit("add_num",this.num,this.message)
    }
    }
    }
    export default Menu;
    </script>

    <template>
    <p>来自父组件的数据 <br>变量{{htotal}}<br>数值:{{total}}<br>字符串:{{title}}</p>
    <hr>
    <p>要发送给父组件的数据,num={{num}}</p>
    <button @click="add">{{ num }}</button>

  2. 父组件中,声明一个方法用于在自定义事件发生时用于获取数据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <template>
    <h1>Home页面</h1>
    <button @click="total++">Home的total:{{total}}</button>
    <!-- 父组件要发送数据给子组件,通过组件的属性传递数据,属性名就是将来的变量名,传递则需要在属性左边加上:-->
    <Menu :htotal="total" total="1100" title="来自home的数据呀!" @add_num="get_data"></Menu>
    </template>
    <script>
    import Menu from "@/components/Menu.vue";
    const Home = {
    name: 'Home',
    data() {
    return {
    total: 0
    }
    },
    components:{
    Menu,
    },
    methods:{
    get_data(num,message){
    console.log("子组件的数据过来了!!");
    console.log(`num=${num},message=${message}`)

    }
    }
    }
    export default Home;
    </script>

本破站由 @BXZDYG 使用 Stellar 主题创建。
本博客部分素材来源于网络,如有侵权请联系1476341845@qq.com删除
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本"页面"访问 次 | 👀总访问 次 | 总访客
全部都是博主用心学编写的啊!不是ai啊 只要保留原作者姓名并在基于原作创作的新作品适用同类型的许可协议,即可基于非商业目的对原作重新编排、改编或者再创作。