一、组件化编程

使用组件编程至少需要三步:

  • 定义组件
  • 注册组件
  • 使用组件

注册组件又分为全局注册局部注册

1.1 全局注册

  1. <body>
  2. <div id="app">
  3. <!-- 3.使用组件 -->
  4. <abc></abc>
  5. <abc></abc>
  6. <abc></abc>
  7. </div>
  8. <!-- 1.定义组件:在组件中如果存在多个标签,那么这些标签应该在同一个根标签中 -->
  9. <template id="temp">
  10. <div>
  11. <img src="1.png" alt="">
  12. <p>功能菜单名字</p>
  13. </div>
  14. </template>
  15. <script>
  16. // 2.注册组件 - 全局注册
  17. //参数1:组件的名字 参数2:将哪个template注册成组件
  18. Vue.component("abc",{
  19. template:"#temp"
  20. });
  21. new Vue({
  22. el:"#app"
  23. });
  24. </script>
  25. </body>

1.2 局部注册

<body>
    <div id="app">
        <abc></abc>
        <abc2></abc2>
    </div>
    <!-- 1.定义组件:在组件中如果存在多个标签,那么这些标签应该在同一个根标签中 -->
    <template id="temp1">
        <div>
            <img src="1.png" alt="">
            <p>功能菜单名字</p>
        </div>
    </template>
    <template id="temp2">
        <div>
            <img src="1.png" alt="">
            <p>功能菜单名字2</p>
        </div>
    </template>
    <script>
        //2.创建组件
        const abc = {
            template : "#temp1"
        }
        const abc2 = {
            template : "#temp2"
        }
        new Vue({
            el:"#app",
            //3.注册组件 - 局部注册
            components:{
                abc,
                abc2
            }
        });
    </script>
</body>

1.3 子组件获取数据

<body>
    <div id="app">
        <!-- 3.使用组件 -->
        <goods></goods>
        <goods></goods>
    </div>
    <!-- 1.定义组件 -->
    <!-- 重点:1.组件默认情况下永远都是获取到本组件的属性值
            2.在子组件中定义数据,必须放在data()方法中,而且必须返回一个对象{key:value,key:value}
    -->
    <template id="temp">
        <div>
            <img :src="url" alt="">
            <p>{{name}}</p>
            <input type="text" v-model="name"><br>
        </div>
    </template>

    <script>
        // 2.注册组件
        Vue.component("goods",{
            template : "#temp",
            // 在子组件中data必须是函数、方法,不能是属性,数据存放在return的对象中
            data(){
                // 返回的永远是一个新的对象
                return {
                    url:"1.png",
                    name:"摇曳露营"
                }
            }
        });
        new Vue({
            el:"#app"
        });
    </script>
</body>

注意:**组件默认情况下永远都是获取到本组件中的属性值;在子组件中定义数据,必须放在data()方法中,而且必须要返回一个对象{key:value,key:value}**

1.4 父子组件之间的通信

1.4.1 父传子

在子组件中通过props指定属性,然后再通过v-bind给其绑定值

<body>
    <div id="app">
        <goods v-bind:cgoods="goods" v-for="goods in goodsList"></goods>
    </div>

    <template id="temp">
        <div>
            <img :src="cgoods.url" alt="">
            <p>{{cgoods.name}}</p>
            <input type="text" v-model="cgoods.name"><br>
        </div>
    </template>

    <script>
        Vue.component("goods",{
            template : "#temp",
            props:{
                cgoods : {
                    //设置默认值,如果没有传值才会显示
                    default:{url:"1.png",name:"摇曳露营-默认"}
                }
            }
        });
        new Vue({
            el:"#app",
            data:{
                goodsList:[
                    {url:"1.png",name:"摇曳露营1"},
                    {url:"1.png",name:"摇曳露营2"},
                    {url:"1.png",name:"摇曳露营3"}
                ]                
            }
        });
    </script>
</body>

注意:如果父组件传进来多个数组,props要用数组接收
例如:
**
父组件:

      <!-- 使用子组件 -->
      <activitydetail :activity="currentActivity"
                      :userData="currentUsers">
      </activitydetail>

子组件:

<script>
  export default {
    props: [
        //父组件传入多个数据用数组接收
        'activity',
        'userData'
    ],
  }
</script>

执行逻辑:
image.png

1.4.2 子传父

通过自定义事件实现数据传递
在子组件中给click事件绑定方法getData(),并在该方法中通过this.$emit()方法发出事件、传递数据

<body>
    <div id="app">
        <!-- 自定义事件绑定的方法,如果需要获取到子组件传递的数据,那么不要写(),
            如果写了在vue看来该方法是无参方法,数据就不会被获取到 -->
        <goods v-bind:cgoods="goods" v-for="goods in goodsList" @abc="childClick"></goods>
    </div>

    <template id="temp">
        <div>
            <img :src="cgoods.url" alt="" @click="getData()">
            <p>{{cgoods.name}}</p>
            <input type="text" v-model="cgoods.name"><br>
        </div>
    </template>

    <script>
        Vue.component("goods",{
            template : "#temp",
            props:{
                cgoods : {
                    //设置默认值,如果没有传值则显示
                    default:{id:1000,url:"1.png",name:"摇曳露营-默认"}
                }
            },
            methods:{
                getData(){
                    //将id传给父组件:自定义事件    $emit用来发出事件并且可以传出数据->父组件
                    this.$emit("abc",this.cgoods.id);
                }
            }
        });
        new Vue({
            el:"#app",
            data:{
                goodsList:[
                    {id:1001,url:"1.png",name:"摇曳露营1"},
                    {id:1002,url:"1.png",name:"摇曳露营2"},
                    {id:1003,url:"1.png",name:"摇曳露营3"}
                ]                
            },
            methods:{
                childClick(id){
                    console.log(id);
                }
            }
        });
    </script>
</body>
  • 在app div中添加事件监听,并调用vue对象中的childClick方法

注意:**自定义事件绑定的方法,如果需要获取到子组件传递的数据,那么不要写(),如果写了在vue看来该方法是一个无参方法,数据就不会被获取到
例如:**

<goods v-bind:cgoods="goods" v-for="goods in goodsList" @abc="childClick"></goods>

执行逻辑:
image.png

1.5 插槽

作用:提升组件的扩展性。
很多时候组件之间的差异非常小,那么在代码层面就会出现很大的冗余,而且每个组件的标签都是不可变的,组件的扩展性就非常低,这些问题可以通过使用插槽来解决

1.5.1 匿名插槽(没有名字的插槽)

<body>
  <div id="app">
    <temp>
      <!-- 指定标签替换插槽中的内容(包括标签)  默认去替换匿名插槽的内容 -->
      <h4>父组件自己指定的显示数据的标签</h4>
    </temp>
  </div>
  <template id="temp">
    <div>
      <h3>子组件</h3>
      <!-- 匿名插槽 -->
      <slot>
        <p>要显示的内容</p>
      </slot>
    </div>
  </template>
  <script>
    Vue.component("temp",{
      template:"#temp"
    });
    new Vue({
      el:"#app"
    });
  </script>
</body>

1.5.2 具名插槽(具有名字的插槽,精确替换)

<body>
  <div id="app">
    <temp>
      <!-- 指定替换哪个插槽中的内容 -->
      <h4 slot="abc">替换具名插槽中的内容</h4>
    </temp>
  </div>
  <template id="temp">
    <div>
      <h3>子组件</h3>
      <!-- 具名插槽 -->
      <slot name="abc">
        <p>具名插槽内容</p>
      </slot>
    </div>
  </template>
  <script>
    Vue.component("temp",{
      template:"#temp"
    });
    new Vue({
      el:"#app"
    });
  </script>
</body>

1.5.3 作用域插槽(带数据的插槽)

<body>
  <div id="app">
    <temp>
      <!-- 作用域插槽 -->
      <template slot-scope="slot">
        <p v-for="book in slot.data">{{book.name}}</p>
      </template>
    </temp>
  </div>
  <template id="temp">
    <div>
      <h3>子组件</h3>
      <!-- 作用域插槽:带有数据的插槽   data属性自定义,名字可以随意取  此处是将books的值绑定到data属性上-->
      <slot :data="books">
        <ul>
          <li v-for="book in books">{{book.name}}</li>
        </ul>
      </slot>
    </div>
  </template>
  <script>
    Vue.component("temp",{
      template:"#temp",
      data(){
        return {
          books:[
            {name:"西游记"},
            {name:"钢铁是怎样炼成的"},
            {name:"Java从入门到放弃"}]
        }
      }
    });
    new Vue({
      el:"#app"
    });
  </script>
</body>

执行逻辑
image.png

二、模块化编程

解决各个模块整合代码冲突问题

2.1 简单示例

order.js

let a = 10;

goods.js

let a = 20;

index.html

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- 在导入js的时候告知是一个模块 在运行时需要运行在服务器上 安装live server插件-->
  <script src="order.js"></script>
  <script src="goods.js"></script>
  <script>
    console.log(a);
  </script>
</head>

运行HTML页面会报错,a重复定义,原因是order.js和goods.js中都有名字叫a的变量
解决办法:通过模块化实现,步骤如下
order.js

let a = 10;
//导出
//可以通过给变量一个默认的名字  导入者可以自定义变量名  解决代码冲突
export default {
  a
};

创建main.js导入order和goods模块中的变量

import aa from "./goods.js"
import aaa from "./order.js"
console.log(aa);//此处的aa就是goods中的a
console.log(aaa);//此处的aaa就是order中的a

在index.html引入这三个js文件时指定为模块js

<!-- 在导入js的时候告知是一个模块 在运行时需要运行在服务器上 安装live server插件-->
<script src="order.js" type="module"></script>
<script src="goods.js" type="module"></script>
<script src="main.js" type="module"></script>

打开index.html运行,显示跨域错误,因为此时是用模块开发,需要使用服务器运行,而不能直接通过浏览器打开,所以此处安装”Live Server“插件,然后通过该插件运行

2.2 export导出

详解:可以导出单个变量、多个变量、属性、类

//单独导出某个变量
export let age = 10;
//一次性导出多个变量
let a = 10;
let b = 20;
let c = 30;
export {a,b,c}
//导出方法
export function say() {
  console.log("hello");
}
//导出一个类
export class Teacher{
  teaching(){
    console.log("good good study");
  }
}

main.js导入

import {age,a,b,c,say,Teacher} from "./export.js"
console.log(age);
console.log(c);
//调用方法
say();
//创建对象
let teacher = new Teacher();
teacher.teaching();//调用方法

2.3 import导入

如果需要导入的js文件中有多个export,一个一个导入非常麻烦,此时可以通过*一次性导入

import * as abc from "./export.js"
console.log(new abc.Teacher());

三、脚手架

node.js与vue脚手架安装见文档:脚手架安装.docx

3.1 搭建项目流程解析

vue init webpack 项目名
init 初始化
webpack vue的模板(框架)
53 - Vue组件化编程、模块化编程、脚手架 - 图4
project name 可以再次指定项目名字 此处直接回车
53 - Vue组件化编程、模块化编程、脚手架 - 图5
project description 项目描述 可以指定也可以直接回车
53 - Vue组件化编程、模块化编程、脚手架 - 图6
author 作者,项目创建人 直接回车 不需要配置
53 - Vue组件化编程、模块化编程、脚手架 - 图7
项目构建方式:此处选择第一个大多数人选择方式,这两种方式就代码上有些许的不一样
53 - Vue组件化编程、模块化编程、脚手架 - 图8
是否要使用vue的路由:此处选择n
53 - Vue组件化编程、模块化编程、脚手架 - 图9
是否使用语法校验器ESLint:此处选择n
53 - Vue组件化编程、模块化编程、脚手架 - 图10
是否使用单元测试:此处选择n
53 - Vue组件化编程、模块化编程、脚手架 - 图11
是否采用e2e(端到端)测试方式:此处也是n
53 - Vue组件化编程、模块化编程、脚手架 - 图12
以什么方式来管理项目中需要用到的包:此处采用npm方式,直接回车
53 - Vue组件化编程、模块化编程、脚手架 - 图13
创建完毕
53 - Vue组件化编程、模块化编程、脚手架 - 图14

3.2 项目结构说明

image.png
build webpack配置
config vue基本配置
node-modules vue依赖包
src 源代码
image.png
assets 静态资源(js、css)
components 组件
App.vue App组件(demo)
main.js 程序入口,加载组件
static 静态资源文件(图片、json数据)
index.html 首页
package.json 项目基本信息

3.3 项目结构解析

3.3.1 index.html

项目的首页

<div id="app"></div>

3.3.2 main.js

main.js是整个项目的入口js,最先被加载

  • 创建Vue对象,作用于id为app的div标签上
  • 注册主键:组件名叫做App
  • App通过import导入,从App.vue中导入

    import Vue from 'vue'
    import App from './App'
    Vue.config.productionTip = false
    /* eslint-disable no-new */
    new Vue({
    el: '#app',
    components: { App },
    template: '<App/>'
    })
    

    3.3.3 App.vue

    是整个项目的主要组件,此组件主要包含三个部分(其实每一个vue文件都应该包含这三个部分)

  • template 定义组件

  • script 导出组件、添加数据、添加当前组件事件方法、导入其他的组件
  • style 指定当前组件中的标签的css
    <template>
    <div id="app">
      <img src="./assets/logo.png">
      <HelloWorld/>
    </div>
    </template>
    <script>
    import HelloWorld from './components/HelloWorld'
    export default {
    name: 'App',
    components: {
      HelloWorld
    },
    data(){
    }
    }
    </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>
    

    3.3.4 自定义模块

    1、在components目录下创建home.vue
    <template>
    <!-- 必须要有一个根 -->
    <div>
    <h2>这是我自己的组件</h2>
    <p>{{message}}</p>
    </div>
    </template>
    <script>
    export default {
    name:"abc",
    data(){
      return {
        message:"hello vue"
      }
    }
    }
    </script>
    <style scoped>
    h2{
      color: red;
    }
    </style>
    
    2、在App.vue中导入home模块、注册、使用
    <template>
    <div id="app">
      <!-- 3.使用 -->
      <Home></Home>
    </div>
    </template>
    <script>
    // 1.导入
    import Home from "./components/home"
    export default {
    name: 'App',
    components: {
      // 2.注册
      Home
    }
    }
    </script>
    <style>
    </style>