Skip to content
On this page

Vue-CLI 和组件基础

学习目标

  • 【掌握】Vue中组件的基本概念
  • 【掌握】Vue脚手架项目的创建
  • 【掌握】Vue-CLI的结构目录
  • 【掌握】props配置选项和自定义事件
  • 【掌握】组件上的v-model

现代前端开发都遵从组件化的开发规范,希望将独立的视图区域拆分为单独的组件来维护,Vue中同样提供了强大的组件系统,一起来学习一下Vue中如何来使用组件。

p9EYYcV.png

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>

页面具体布局应该是样子:

p9EYUnU.png

现在希望将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,可以通过自定义事件的形式来实现。

  • 在父组件中,给MyHeader绑定一个自定义事件,事件名称可以任意指定,这里指定为changeMsg

    html
    <my-header class="mh" :msg='msg' @changeMsg='changeMsg'/>

    这个自定义事件需要指定一个事件回调,那么便可以在methods中来实现。在Vue中需要明确一点,数据在哪修改数据的方法就应该在那里。

    vue
    methods:{
          changeMsg(){
            this.msg='hi vue'
          }
    }
  • 在子组件中,通过实例方法this.$emit()来触发绑定的自定义事件,从而执行上面实现的回调函数,完成数据的修改。

    vue
    <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的内容已经被正确修改,并且作于用视图了。

  • this.$emit()这个实例方法第一个参数表示触发的自定义事件名称,后续还可以传入参数,父组件的自定义事件中会接收到这个传入的参数。

    vue
    //子组件中
     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'
}

版权声明 鄂ICP备2022000216号-2