学习目标
- 【掌握】Vue中组件的基本概念
- 【掌握】Vue脚手架项目的创建
- 【掌握】Vue-CLI的结构目录
- 【掌握】props配置选项和自定义事件
- 【掌握】组件上的v-model
现代前端开发都遵从组件化的开发规范,希望将独立的视图区域拆分为单独的组件来维护,Vue中同样提供了强大的组件系统,一起来学习一下Vue中如何来使用组件。
1.Vue的组件
回忆一下之前的课程知识,我们在每个页面中都通过new Vue(options)
的形式创造了一个vm实例,然后通过el
配置项和视图容器关联起来,这个vm就可以理解为整个app的根实例,结合上面的图示,现在我们希望将整个app实例分为3个单独的组件(header content side) 我们通过代码来完成这个布局:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
.header{
background-color: skyblue;
height: 20vh;
}
.content{
float: left;
width: 60%;
height: 80vh;
background-color: gold;
}
.side{
float: right;
width: 40%;
height: 80vh;
background-color: aqua;
}
</style>
</head>
<body>
<div id="app">
<div class="header"><h1>HEADER</h1></div>
<div class="content"><h1>CONTENT</h1></div>
<div class="side"><h1>SIDE</h1></div>
</div>
<script src="/vue.js"></script>
<script>
new Vue({
el: '#app',
})
</script>
</body>
</html>
页面具体布局应该是样子:
现在希望将3个独立的视图区域拆分为3个单独的组件,然后组合到一起,以Header为例,我们一起来完成这个过程。
1.1 定义组件
我们都知道,根实例vm是通过Vue这个全局的构造函数生成,那么现在有一个疑问,组件(之后用vc来统称)是如何生成的呢?通过调用一个全局方法Vue.extend
,我们可以得到一个组件实例,这个方法传入的参数也是配置选项,用来存放对应组件的各个配置选项,这里要特别引入一个全新的配置选项template,这个配置选项用来存放对应的视图模板,所以这个Header我们可以这样定义:
js
const MyHeader = Vue.extend({
name: 'MyHeader',
template: `<div class="header"><h1>HEADER</h1></div>`,
})
在实际开发中,我们会定义大量的组件,为了进一步简化代码,上面的组件定义可以进一步简写,可以省略掉Vue.extend
,直接将组件描述为一个配置对象即可,所以可以写成这样:
js
const MyHeader ={
name: 'MyHeader',
template: `<div class="header"><h1>HEADER</h1></div>`,
}
1.2 注册组件
定义好了组件之后,我们需要在根组件中使用Header,Vue规定必须注册这个组件,Vue提供了2种方法来注册组件。
第一种是全局注册,调用一个全局方法Vue.component()
来实现
js
Vue.component('my-header', MyHeader)//第一个参数是任意指定的组件名称 第二个参数即是刚刚定义好的组件
第二种是局部注册,可以在当前组件中新增一个配置选项components来实现
js
const vm = new Vue({
el: '#app',
components: {
'my-header': MyHeader,
},
})
1.3 使用组件
最后我们便可以使用刚才注册好的组件,我们把之前视图的区域替换成组件。
html
<div id="app">
<my-header></my-header> //这里即是组件
<div class="content"><h1>CONTENT</h1></div>
<div class="side"><h1>SIDE</h1></div>
</div>
最后我们可以打开浏览器,可以看到实现了同样的布局效果,其他2个区域我们也可以采取同样的操作抽离成组件。
1.4 组件使用的注意事项
我们现在已经了解到,一个大型的应用应该是由一个根实例(vm)和多个组件实例(vc)共同组成,无论是根实例还是组件实例都可以理解为一个配置对象,但是组件实例的配置对象和根实例之间存在一些使用上的细小差别。
- 组件实例上的data必须是一个函数
- 组件结构必须有一个根元素
- 组件实例上没有el属性
2.Vue-CLI
虽然我们刚才确实将视图内容拆分了独立组件,但有一个问题想必大家应该感受得到:视图结构必须以字符串的形式写在template选项中,当这个结构很复杂的时候,大量的字符串拼接操作会让开发者非常困扰,Vue也考虑到了这个问题,它给我们提供了一个以.vue
为后缀名的文件格式来解决这个问题,我们一般称之为单文件组件,在深入了解它之前,我们先来介绍一下Vue-CLI
。
Vue-CLI
是官方提供的开发工具,可以利用它快速创建大型的web项目,它通过webpack集中了大量功能,免去了开发者手动搭建开发环境时的各种问题,我们一起来看看如何利用它来一步步完成项目的开发。
2.1 起步
通过NPM命令来安装依赖:
npm install -g @vue/cli
# OR
yarn global add @vue/cli
然后创建一个项目
vue create app //app是可以任意指定的项目名称
在创建项目的过程中,大家会看到一些选项提示,选择Vue2版本的最基础配置即可。
2.2 项目结构分析
安装完成后,我们会看到一个生成的项目结构,我们一起来分析一下这里面的目录文件都是用来做什么的。
-
node_modules 用来存放第三方依赖的目录
-
public 公共页面目录 一般不会改动
- favicon.ico 网站的偏爱图标
- index.html 根实例挂载的视图容器
-
src 项目的代码开发目录
- assets 存放静态资源目录 比如图片 字体文件等等 可以任意指定目录名称
- components 组件目录
- App.vue 根组件
- main.js 项目的入口文件
- .gitignore git的忽略文件
- babel.config.js babel配置文件
- jsconfig.json js编译时的配置文件
- package.json 项目的说明书
- README.md 项目的说明文档
- vue.config.js 脚手架的配置文件
大部分配置文件目前都不用更改配置,在后续课程中如果需要会做进一步说明。
2.3 单文件组件
Vue给我们提供了一个专门以.vue
为后缀名的文件形式来书写组件,以刚才项目中App.vue为例,来具体分析一下这个文件的结构。
vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js App" />
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld,
},
}
</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>
- template 可以理解为为视图区域 在里面书写各种html结构
- script 可以理解为逻辑部分 在里面书写配置选项
- style 可以理解为样式部分 在里面书写样式
这种结构形式非常清晰地表达出了一个web组件的组成部分,所以绝大多数Vue应用的组件都会以这个单文件组件的形式来书写。
2.4 项目启动
在脚手架创建的项目中必须通过命令来启动:
npm run serve
启动成功后,我们在控制台可以看到提供的端口号信息:
打开8080端口后,我们看到一个默认的vue启动页:
在src对应的位置,我们可以任意来修改它,比如就更换成我们刚才写过的那个布局结构,我们可以这样做:
-
清空components目录之前的预设组件,新建3个组件分别是
MyHeader
MyContent
MySide
(组件命令我们采用的规范是大驼峰或者短横线连接) -
然后在根组件App.vue中按照引入 注册 使用的步骤完成操作。
vue<template> <div id="app"> <my-header class="mh" /> <my-content class="mc" /> <my-side class="ms" /> </div> </template> <script> import MyContent from './components/MyContent.vue' import MyHeader from './components/MyHeader.vue' import MySide from './components/MySide.vue' export default { name: 'App', components: { MyContent, MySide, MyHeader, }, } </script> <style> * { padding: 0; margin: 0; } .mh { background-color: aqua; height: 20vh; } .mc { background-color: pink; height: 80vh; float: left; width: 60%; } .ms { background-color: gold; float: right; width: 40%; height: 80vh; } </style>
3.props配置选项
接下来一起来学习一个组件实例中非常重要的配置选项props。
现在希望在3个布局组件中展示动态展示一句文本hello vue
,很多同学会认为在3个组件的data中定义一个数据然后渲染到视图中即可,很简单呀,这么做确实没问题,如果现在有10个组件,或者更多呢,这样做显然效率不佳,如果希望同步修改这个文本,那需要去到每个组件中修改data数据,那会非常麻烦。
vue中提供了一种父组件向子组件传递数据的方式,比如根实例APP希望向3个子组件传递数据,可以这么来做:
vue
<template>
<div id="app">
<my-header class="mh" :msg='msg'/> //需要传递给子组件的属性直接写在标签上
<my-content class="mc" :msg='msg'/>
<my-side class="ms" :msg='msg'/>
</div>
</template>
<script>
import MyContent from './components/MyContent.vue'
import MyHeader from './components/MyHeader.vue'
import MySide from './components/MySide.vue'
export default {
name: 'App',
data() {
return {
msg:'hello vue'
}
},
components: {
MyContent,
MySide,
MyHeader,
},
}
</script>
<style>
* {
padding: 0;
margin: 0;
}
.mh {
background-color: aqua;
height: 20vh;
}
.mc {
background-color: pink;
height: 80vh;
float: left;
width: 60%;
}
.ms {
background-color: gold;
float: right;
width: 40%;
height: 80vh;
}
</style>
在子组件中,需要配置props这个配置选项在接受父组件传过来的数据,然后便可以在视图中直接使用了。
以MyHeader组件为例:
vue
<template>
<div>
<h1>HEADER</h1>
<p>{{msg}}</p>
</div>
</template>
<script>
export default {
props:['msg'] //props中以数组的形式接受父组件传过来的数据
}
</script>
<style>
</style>
在视图中,我们也可以看到根组件中的msg内容渲染到了各个子组件中,如果希望修改这个数据,直接操作根组件中的数据即可。
4.父组件的自定义事件
我们刚才成功地将父组件中的数据传递给了子组件,并且说明了如果希望修改数据可以在父组件中直接修改即可,但现在希望在MyHeader
组件中通过点击一个button按钮来将文本改为hi vue
。
如何在子组件中修改父组件的数据呢?Vue规定不能直接在子组件中修改props,可以通过自定义事件的形式来实现。
-
在父组件中,给
htmlMyHeader
绑定一个自定义事件,事件名称可以任意指定,这里指定为changeMsg
<my-header class="mh" :msg='msg' @changeMsg='changeMsg'/>
这个自定义事件需要指定一个事件回调,那么便可以在methods中来实现。在Vue中需要明确一点,数据在哪修改数据的方法就应该在那里。
vuemethods:{ changeMsg(){ this.msg='hi vue' } }
-
在子组件中,通过实例方法
vuethis.$emit()
来触发绑定的自定义事件,从而执行上面实现的回调函数,完成数据的修改。<template> <div> <h1>HEADER</h1> <p>{{msg}}</p> <button @click='changeMsg'>点击修改msg</button> </div> </template> <script> export default { props:['msg'], methods: { changeMsg(){ this.$emit('changeMsg') //注意区分 每个changeMsg代表的具体含义 } }, } </script> <style> </style>
在
MyHeader
中点击button,发现msg的内容已经被正确修改,并且作于用视图了。 -
vuethis.$emit()
这个实例方法第一个参数表示触发的自定义事件名称,后续还可以传入参数,父组件的自定义事件中会接收到这个传入的参数。//子组件中 methods: { changeMsg(){ this.$emit('changeMsg','hi vue') } } //父组件中 methods: { changeMsg(val) { this.msg = val }, }
5.组件上的v-model
指令
v-model
指令不仅可以用在标签上,还可以用在组件身上,它出同时触发2个行为:
- 向子组件传递一个prop名为value的值
- 绑定一个事件名为input的自定义事件
在我们的项目中,将这个指令绑定在MyContent
组件上:
vue
<my-content class="mc" :msg="msg" v-model="text"/>
//在data中定义text这项数据
data() {
return {
msg: 'hello vue',
text:'hello peiqi'
}
}
在子组件中可以接受prop值和触发自定义事件:
vue
<template>
<div>
<h1>CONTENT</h1>
<p>{{ msg }}</p>
<p>{{ value }}</p>
<!-- 默认绑定一个input事件 触发事件传递的参数会修改父组件中原始数据text -->
<button @click="$emit('input', 'hello qiaozhi')">修改text</button>
</div>
</template>
<script>
export default {
props: ['msg', 'value'], //默认接受一个名为value的prop
}
</script>
<style></style>
在子组件中,可以通过model配置选项来手动设定传入的属性名和事件名:
vue
model: {
prop: 'checked',
event: 'change'
}