zoukankan      html  css  js  c++  java
  • Vue全家桶--08 Vue组件化开发

    Vue全家桶--08 Vue组件化开发

    8.1 组件的概念

    组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:

     在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。在 Vue 中注册组件很简单:

    // 定义名为 todo-item 的新组件
    Vue.component('todo-item', {
      template: '<li>这是个待办项</li>'
    })
    
    var app = new Vue(...)

    现在你可以用它构建另一个组件模板:

    <ol>
      <!-- 创建一个 todo-item 组件的实例 -->
      <todo-item></todo-item>
    </ol>

     **组件就是对局部视图的封装,每个组件包含了:

      HTML结构

      CSS样式

      JavaScript行为:data数据/methods行为

    **提高开发效率,增强可维护性,更好的去解决软件上的高耦合、低内聚、无重用的3大代码问题

    **Vue 中的组件思想借鉴于 React

    **目前主流的前端框架:Angular、React 、Vue 都是组件化开发思想

    8.2 组件的基本使用

     为了能够在模板中使用,这些组件必须先注册以便Vue能够识别,注册类型:全局注册和局部注册

    **全局注册

    Vue.component('组件名',{
    template: '定义组件模板',
    data: function(){ //data 选项在组件中必须是一个函数
    return {}
    }
    //其他选项:methods
    })

    组件名:可使用驼峰(camelCase)或者横线分隔(kebab-case)命名方式

        但DOM中只能使用横线分隔方式进行引用组件

        官方强烈推荐组件名字母全小写且必须包含一个连字符

    template:定义组件的视图模板

    data:在组件中必须是一个函数

    <body>
    
        <div id="app">
            <component-a></component-a>
        </div>
    
        <script src="./node_modules/vue/dist/vue.js"></script>
        <script>
            /*
            全局组件注册:
                它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
            参数1:组件名
                1.可使用驼峰(camelCase)或者横线分隔(kebab-case)命名方式
                2.DOM 中只能使用横线分隔方式进行引用组件
            */
            Vue.component('component-a', {
                // template 选项指定此组件的模板代码
                template: `<div><h3>头部组件--{{ name }}</h3></div>`,
                // data 必须是函数
                data: function () {
                    return {
                        name: 'hello Vue!'
                    }
                }
            })
    
            var vm = new Vue({
                el: '#app'
            });
        </script>
    </body>

    **局部注册

    一般把一些非通用部分注册为局部组件,一般只适用于当前项目的

    1. JS 对象来定义组件:
    var ComponentA = { data: function(){}, template: '组件模板A'}
    var ComponentA = { data: function(){}, template: '组件模板A'}
    2. 在Vue实例中的 components 选项中引用组件:
    new Vue({
      el: '#app',
      data: {},
      components: { // 组件选项
        'component-a': ComponentA // key:组件名,value: 引用的组件对象
        'component-b': ComponentB
      }
    })
    <body>
        <div id="app">
            <component-b></component-b>
        </div>
        <script src="./node_modules/vue/dist/vue.js"></script>
        <script>
            var ComponentB = {
                template: '<div> 这是:{{ name }}</div>',
                data: function () {
                    return {
                        name: '局部组件'
                    }
                }
            }
            var vm = new Vue({
                el: '#app',
                components: {
                    'component-b': ComponentB
                }
            });
        </script>
    </body>

    **总结:

    *组件是可复用的 Vue 实例,不需要手动实例化

    *与 new Vue 接收相同的选项,例如 data 、computed 、watch 、methods 等

    *组件的 data 选项必须是一个函数

    8.3 多个组件示例

     组件js文件:

    Header.js
    Vue.component('app-header',{
        template:`<h1>头部组件--{{ content }}</h1>`,
        data() {
            return {
                content: 'header-value',
            };
        },
    })
    Main.js
    Vue.component('app-main', {
        template: `<ul>
                    <li v-for="(item,index) in content">{{ item.name }}</li>
                  </ul>`,
        data () {
            return {
                content: [
                    { id: 1, name: '客户管理' },
                    { id: 2, name: '供应商管理' },
                    { id: 3, name: '信息管理' }
                ]
            };
        },
    })
    Footer.js
    Vue.component('app-footer',{
        template:`<h3>底部组件--{{ property }}</h3>`,
        data () {
            return {
                property: 'footer-value',
            };
        },
    })

    html

    <body>
        <div id="app">
            <app-header></app-header>
            <app-main></app-main>
            <app-footer></app-footer>
        </div>
    
        <script src="./node_modules/vue/dist/vue.js"></script>
        <script src="./components/Header.js"></script>
        <script src="./components/Main.js"></script>
        <script src="./components/Footer.js"></script>
        <script>
            var vm = new Vue({
                el:'#app'
            });
        </script>
    
    </body>

    8.4 Bootstrap首页组件化(重点)

    8.4.1 首页分析

    首页拆分多个组件:
        a.头部导航区域 -- AppNavbar
        b.左边菜单栏区域 -- AppLeft
        c.右边主区域 -- AppHome
        d.DashBoard
        e.HomeList

    8.4.2 头部导航组件注册 AppNavbar

    AppNavbar.js :声明自调用函数;并且声明window属性,否则函数之外无法调用

    ;(function(){
    
        const template=`<nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container-fluid">
          <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
              aria-expanded="false" aria-controls="navbar">
              <span class="sr-only">Toggle navigation</span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">{{ title }}</a>
          </div>
          <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
              <li><a href="#">Dashboard</a></li>
              <li><a href="#">Settings</a></li>
              <li><a href="#">Profile</a></li>
              <li><a href="#">Help</a></li>
            </ul>
            <form class="navbar-form navbar-right">
              <input @blur="searchBlur" type="text" class="form-control" placeholder="Search...">
            </form>
          </div>
        </div>
      </nav>`;
    
        window.AppNavbar ={
            template,
            data() {
                return {
                    title: '梦学谷',
                };
            },
            methods:{
                searchBlur(){
                    alert('失去焦点!');
                }
            }
    
        }
    
    })()

    index.html 页面引用 AppNavbar.js,并声明组件

    <script src="./components/AppNavbar.js"></script>
     <!--头部导航区域-->
        <app-navbar></app-navbar>

    8.4.3 左侧导航组件注册 AppLeft

    ;(function(){
    
        const template=`<div class="col-sm-3 col-md-2 sidebar">
        <ul class="nav nav-sidebar">
          <li class="active"><a href="#">Overview <span class="sr-only">(current)</span></a></li>
          <li><a href="#">Reports</a></li>
          <li><a href="#">Analytics</a></li>
          <li><a href="#">Export</a></li>
        </ul>
        <ul class="nav nav-sidebar">
          <li><a href="">Nav item</a></li>
          <li><a href="">Nav item again</a></li>
          <li><a href="">One more nav</a></li>
          <li><a href="">Another nav item</a></li>
          <li><a href="">More navigation</a></li>
        </ul>
        <ul class="nav nav-sidebar">
          <li><a href="">Nav item again</a></li>
          <li><a href="">One more nav</a></li>
          <li><a href="">Another nav item</a></li>
        </ul>
      </div>`;
    
        window.AppLeft={
            template
        }
    
    })()
    <script src="./components/AppLeft.js"></script>
    <!--左边菜单栏区域-->
            <app-left></app-left>

    8.4.4 右侧主页面

    分为3个组件:AppHome/DashBoard/HomeList

    AppHome为父组件,DashBoard和HomeList为子组件

    AppHome.js

    ;(function(){
    
        const template=` <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
    
        <!--右边上半区域-->
        <h1 class="page-header">Dashboard</h1>
        <dash-board></dash-board>
    
        <!--右边下半区域-->
        <h2 class="sub-header">Section title</h2>
        <home-list></home-list>
      </div>`;
    
        window.AppHome={
            template,
            components:{
                DashBoard,
                HomeList
            }
        }
    
    })()

    DashBoard.js

    ;(function(){
    
        const template=`<div class="row placeholders">
        <div class="col-xs-6 col-sm-3 placeholder">
          <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200"
            height="200" class="img-responsive" alt="Generic placeholder thumbnail">
          <h4>Label</h4>
          <span class="text-muted">Something else</span>
        </div>
        <div class="col-xs-6 col-sm-3 placeholder">
          <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200"
            height="200" class="img-responsive" alt="Generic placeholder thumbnail">
          <h4>Label</h4>
          <span class="text-muted">Something else</span>
        </div>
        <div class="col-xs-6 col-sm-3 placeholder">
          <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200"
            height="200" class="img-responsive" alt="Generic placeholder thumbnail">
          <h4>Label</h4>
          <span class="text-muted">Something else</span>
        </div>
        <div class="col-xs-6 col-sm-3 placeholder">
          <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200"
            height="200" class="img-responsive" alt="Generic placeholder thumbnail">
          <h4>Label</h4>
          <span class="text-muted">Something else</span>
        </div>
      </div>`;
    
        window.DashBoard={
            template
        }
    
    })()

    HomeList.js

    ;(function(){
    
        const template=`<div class="table-responsive">
        <table class="table table-striped">
          <thead>
            <tr>
              <th>#</th>
              <th>Header</th>
              <th>Header</th>
              <th>Header</th>
              <th>Header</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>1,001</td>
              <td>Lorem</td>
              <td>ipsum</td>
              <td>dolor</td>
              <td>sit</td>
            </tr>
            <tr>
              <td>1,002</td>
              <td>amet</td>
              <td>consectetur</td>
              <td>adipiscing</td>
              <td>elit</td>
            </tr>
            <tr>
              <td>1,003</td>
              <td>Integer</td>
              <td>nec</td>
              <td>odio</td>
              <td>Praesent</td>
            </tr>
            <tr>
              <td>1,003</td>
              <td>libero</td>
              <td>Sed</td>
              <td>cursus</td>
              <td>ante</td>
            </tr>
            <tr>
              <td>1,004</td>
              <td>dapibus</td>
              <td>diam</td>
              <td>Sed</td>
              <td>nisi</td>
            </tr>
            <tr>
              <td>1,005</td>
              <td>Nulla</td>
              <td>quis</td>
              <td>sem</td>
              <td>at</td>
            </tr>
            <tr>
              <td>1,006</td>
              <td>nibh</td>
              <td>elementum</td>
              <td>imperdiet</td>
              <td>Duis</td>
            </tr>
            <tr>
              <td>1,007</td>
              <td>sagittis</td>
              <td>ipsum</td>
              <td>Praesent</td>
              <td>mauris</td>
            </tr>
            <tr>
              <td>1,008</td>
              <td>Fusce</td>
              <td>nec</td>
              <td>tellus</td>
              <td>sed</td>
            </tr>
            <tr>
              <td>1,009</td>
              <td>augue</td>
              <td>semper</td>
              <td>porta</td>
              <td>Mauris</td>
            </tr>
            <tr>
              <td>1,010</td>
              <td>massa</td>
              <td>Vestibulum</td>
              <td>lacinia</td>
              <td>arcu</td>
            </tr>
            <tr>
              <td>1,011</td>
              <td>eget</td>
              <td>nulla</td>
              <td>Class</td>
              <td>aptent</td>
            </tr>
            <tr>
              <td>1,012</td>
              <td>taciti</td>
              <td>sociosqu</td>
              <td>ad</td>
              <td>litora</td>
            </tr>
            <tr>
              <td>1,013</td>
              <td>torquent</td>
              <td>per</td>
              <td>conubia</td>
              <td>nostra</td>
            </tr>
            <tr>
              <td>1,014</td>
              <td>per</td>
              <td>inceptos</td>
              <td>himenaeos</td>
              <td>Curabitur</td>
            </tr>
            <tr>
              <td>1,015</td>
              <td>sodales</td>
              <td>ligula</td>
              <td>in</td>
              <td>libero</td>
            </tr>
          </tbody>
        </table>
      </div>`;
    
        window.HomeList={
            template
        }
    
    })()

    index.html

    <body>
    
      <div id="app">
    
        <!--头部导航区域-->
        <app-navbar></app-navbar>
    
        <!--核心区域:分左右两边-->
        <div class="container-fluid">
          <div class="row">
    
            <!--左边菜单栏区域-->
            <app-left></app-left>
    
            <!--右边主页面区域: 分上下两个区域-->
            <app-home></app-home>
    
          </div>
        </div>
    
      </div>
    
      <script src="../node_modules/vue/dist/vue.js"></script>
      <script src="./components/AppNavbar.js"></script>
      <script src="./components/AppLeft.js"></script>
      <script src="./components/Home/DashBoard.js"></script>
      <script src="./components/Home/HomeList.js"></script>
      <script src="./components/Home/AppHome.js"></script>
      <script>
        var vm = new Vue({
          el: '#app',
          components:{
            AppNavbar,
            AppLeft,
            AppHome
          }
    
        });
      </script>
    
    </body>

     8.4.5 极致组件化

    根组件提取 App.js注意:template 模板中必须要的根元素,所以要在提取的内容外层加上 <div></div> , 一定要不要少了,不然报错!

    报错提示:

    App.js 将之前的组件AppHome/AppNavbar/AppLeft 声明在App.js中

    ; (function () {
    
        const template = `
        
            <!--头部导航区域-->
            <app-navbar></app-navbar>
    
            <!--核心区域:分左右两边-->
            <div class="container-fluid">
            <div class="row">
    
                <!--左边菜单栏区域-->
                <app-left></app-left>
    
                <!--右边主页面区域: 分上下两个区域-->
                <app-home></app-home>
    
            </div>
            </div>
        
        `;
    
        window.App = {
            template,
            components:{
                AppNavbar,
                AppLeft,
                AppHome
              }
        }
    
    })()

    index.html 页面引用App.js ,并且声明App的组件

    <body>
    
      <div id="app">
    
       <app></app>
    
      </div>
    
      <script src="../node_modules/vue/dist/vue.js"></script>
      <script src="./components/AppNavbar.js"></script>
      <script src="./components/AppLeft.js"></script>
      <script src="./components/Home/DashBoard.js"></script>
      <script src="./components/Home/HomeList.js"></script>
      <script src="./components/Home/AppHome.js"></script>
    
      <script src="./App.js"></script>
      <script>
        var vm = new Vue({
          el: '#app',
          components:{
            App
          }
        });
      </script>
    
    </body>

    注意:在 <div id="app"> 下通过 </app> 引用 App 组件不是很好, 因为页面代码中会多出一个 div.
    更好的方式是,在 <div id="app"> 下无须使用 </app> 引用 App 组件,
    可以通过 Vue 根实例的 template 选项引用组件 <app></app> 后,然后会把 template 中的渲染结果替换掉
    #app 标签。

    当前 index.html 文件中还有 JS 代码,可以将这些 JS 代码提取出来放到 main.js 中

    main.js 
    var vm = new Vue({
        el: '#app',
        template:'<app></app>',
        components:{
          App
        }
      });

    index.html

    <body>
    
      <div id="app">
    
      </div>
    
      <script src="../node_modules/vue/dist/vue.js"></script>
      <script src="./components/AppNavbar.js"></script>
      <script src="./components/AppLeft.js"></script>
      <script src="./components/Home/DashBoard.js"></script>
      <script src="./components/Home/HomeList.js"></script>
      <script src="./components/Home/AppHome.js"></script>
    
      <script src="./App.js"></script>
      <script src="./main.js"></script>
    
    </body>

    8.5 组件化注意事项

    **组件可以理解为特殊的 Vue 实例,不需要手动实例化,管理自己的 template 模板
    **组件的 template 必须有且只有一个根节点
    **组件的 data 选项必须是函数,且函数体返回一个对象
    **组件与组件之间是相互独立的,可以配置自己的一些选项资源 data、methods、computed 等等
    **思想:组件自己管理自己,不影响别人

    8.6 Vue父子组件间通信

    8.6.1 组件间通信方式

    1. props 父组件向子组件传递数据
    2. $emit 自定义事件
    3. slot 插槽分发内容

    8.6.2 组件间通信规则

    1. 不要在子组件中直接修改父组件传递的数据。
    2. 数据初始化时,应当看初始化的数据是否用于多个组件中,如果需要被用于多个组件中,则初始化在父组件
    中;如果只在一个组件中使用,那就初始化在这个要使用的组件中。
    3. 数据初始化在哪个组件, 更新数据的方法(函数)就应该定义在哪个组件。

    8.6.3 props向子组件传递数据

      8.6.3.1 声明组件对象中定义 props(子组件中声明)

      在声明组件对象中使用 props 选项指定

    const MyComponent = {
        template: '<div></div>',
        props: 此处值有以下3种方式,
        components: {
        }
    }

      方式1:指定传递属性名,注意是 数组形式

    props: ['id','name','salary', 'isPublished', 'commentIds', 'author', 'getEmp']

      方式2:指定传递属性名和数据类型,注意是 对象形式

    props:{
      id:Number,
      name:String,
      salary:Number,
      isPublished:Boolean,
      commentIds:Array,
      author:Object,
      getEmp:Function    
    }

      方式3:指定属性名、数据类型、必要性、默认值

    props: {
    name: {
    type: String,
    required: true,
    default: 'mxg'
    }
    }

      8.6.3.2 引用组件时动态赋值(父组件)

      在引用组件时,通过 v-bind 动态赋值

    <my-component v-bind:id="2" :name="meng" :salary="9999" :is-published="true" :comment-ids="[1, 2]"
    :author="{name: 'alan'}" :get-emp="getEmp" >
    </my-component>

      8.6.3.3 传递数据注意

      1. props 只用于父组件向子组件传递数据
      2. 所有标签属性都会成为组件对象的属性, 模板页面可以直接引用
      3. 问题:
        a. 如果需要向非子后代传递数据,必须多层逐层传递
        b. 兄弟组件间也不能直接 props 通信, 必须借助父组件才可以

    8.6.4 props案例 -- 列表渲染

    渲染DashBoard和HomesList数据,由父组件AppHome定义数据

    AppHome.js  --调用子组件 v-bind传参

    ; (function () {
    
        const template = ` <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
    
        <!--右边上半区域-->
        <h1 class="page-header">Dashboard</h1>
        <dash-board :hobbies='hobbies'></dash-board>
    
        <!--右边下半区域-->
        <h2 class="sub-header">Section title</h2>
        <home-list :empList='empList'></home-list>
      </div>`;
    
        window.AppHome = {
            template,
            components: {
                DashBoard,
                HomeList
            },
            data() {
                return {
                    hobbies: ['看书', '台球', '睡觉', '撸代码'],//dashboard显示数据
                    empList: [//homelist 显示数据
                        { id: 1, name: '小梦1', salary: 80001 },
                        { id: 2, name: '小梦2', salary: 80002 },
                        { id: 3, name: '小梦3', salary: 80003 },
                        { id: 4, name: '小梦4', salary: 80004 }
                    ]
                };
            },
        }
    
    })()

    DashBoard.js

    ;(function(){
    
        const template=`
        <div class="row placeholders">
            <div v-for='(item,index) in hobbies' class="col-xs-6 col-sm-3 placeholder">
            <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200"
                height="200" class="img-responsive" alt="Generic placeholder thumbnail">
            <h4>{{item}}</h4>
            <span class="text-muted">Something else</span>
            </div>
        </div>
        `;
    
        window.DashBoard={
            template,
            props:['hobbies'] //定义父组件传过来的数据
        }
    
    })()

    HomeList.js 

    **将HomeList组件中的 tr 在单独封装一个组件,取名Item

    Item.js  -- 定义props 定义父组件传过来的参数

    ;(function(){
    
        const template=`
        <tr>
            <td>{{ emp.id }}</td>
            <td>{{ emp.name }}</td>
            <td>{{ emp.salary }}</td>
        </tr>
        `;
    
        window.Item={
            template,
            props:{//
                emp:Object
            }
        }
    
    })()

    HomeList.js  --

    ;(function(){
    
        const template=`<div class="table-responsive">
        <table class="table table-striped">
          <thead>
            <tr>
              <th>id</th>
              <th>name</th>
              <th>salary</th>
            </tr>
          </thead>
          <tbody>
            <item v-for='(emp,index) in empList' :emp='emp'></item>
          </tbody>
        </table>
      </div>`;
    
        window.HomeList={
            template,
            props:{ //接收父组件传过来的数据,然后在将数组里面的对象传给子组件
                empList:Array
            },
            components:{
                Item
            }
        }
    
    })()

    8.6.5 props案例 -- 删除员工功能实现

    根据组件通信的原则,在哪个组件定义的数据就在哪个组件上定义相关方法!!!

    因为要对emplist数据在删除,所在在AppHome组件上定义删除的方法,并且传递给子组件

    ; (function () {
    
        const template = ` <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
    
        <!--右边上半区域-->
        <h1 class="page-header">Dashboard</h1>
        <dash-board :hobbies='hobbies'></dash-board>
    
        <!--右边下半区域-->
        <h2 class="sub-header">Section title</h2>
        <home-list :empList='empList' :delItem="delItem"></home-list>
      </div>`;
    
        window.AppHome = {
            template,
            components: {
                DashBoard,
                HomeList
            },
            data() {
                return {
                    hobbies: ['看书', '台球', '睡觉', '撸代码'],//dashboard显示数据
                    empList: [//homelist 显示数据
                        { id: 1, name: '小梦1', salary: 80001 },
                        { id: 2, name: '小梦2', salary: 80002 },
                        { id: 3, name: '小梦3', salary: 80003 },
                        { id: 4, name: '小梦4', salary: 80004 }
                    ]
                };
            },
            methods: {
                // 删除指定下标的数据
                // 因为删除 emp 会对 empList 做更新操作,
                // 而 empList 是初始化在当前这个组件里,所以删除的函数要定义在这个组件中
                delItem(index) {
                    this.empList.splice(index, 1);
                }
            }
        }
    
    })()

    **Item组件并不是AppHome组件的子组件,而是孙子组件,所以需要逐层传递,先传给HomeList组件,再由HomeList组件传给Item组件

    HomeList组件

    ;(function(){
    
        const template=`<div class="table-responsive">
        <table class="table table-striped">
          <thead>
            <tr>
              <th>id</th>
              <th>name</th>
              <th>salary</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <item v-for='(emp,index) in empList' :key='emp.id' :emp='emp'  :index='index' :delItem='delItem'></item>
          </tbody>
        </table>
      </div>`;
    
        window.HomeList={
            template,
            props:{ //接收父组件传过来的数据,然后在将数组里面的对象传给子组件
                empList:Array,
                delItem:Function  //逐层传递
            },
            components:{
                Item
            }
        }
    
    })()

    Item组件

    ; (function () {
    
        const template = `
        <tr>
            <td>{{ emp.id }}</td>
            <td>{{ emp.name }}</td>
            <td>{{ emp.salary }}</td>
            <td><a href='#' @click='deleteItem'>删除</a></td>
        </tr>
        `;
    
        window.Item = {
            template,
            props: {//
                emp: Object,
                delItem: Function,
                index: Number
            },
            methods: {
                deleteItem() {
                    // 移除索引为index的一条记录,
                    // 注意:不要少了 this
                    this.delItem(this.index);
                }
            }
        }
    
    })()

    8.6.6 自定义事件

    作用:通过 自定义事件 来代替 props 传入函数形式

    a.绑定自定义事件

    在父组件中定义事件监听函数,并引用子组件标签上 v-on 绑定事件监听。

    // 通过 v-on 绑定
    // @自定义事件名=事件监听函数
    // 在子组件 dashboard 中触发 delete_hobby 事件来调用 deleteHobby 函数
    <dashboard @delete_hobby="deleteHobby"></dashboard>

    b.触发监听事件函数执行

    在子组件中触发父组件的监听事件函数调用

    // 子组件触发事件函数调用
    // this.$emit(自定义事件名, data)
    this.$emit('delete_emp', index)

    c.自定义事件注意

    1. 自定义事件只用于子组件向父组件发送消息(数据)

    2. 隔代组件或兄弟组件间通信此种方式不合适

    8.6.7 自定义事件案例

    删除DashBoard中的图标元素

    在父组件AppHome中定义删除函数deleteHobby,并通过 v-on自定义事件传递给子组件

    AppHome.js

    ; (function () {
    
        const template = ` <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
    
        <!--右边上半区域-->
        <h1 class="page-header">Dashboard</h1>
        <dash-board :hobbies='hobbies' @del-Hobby="deleteHobby"></dash-board>
    
        <!--右边下半区域-->
        <h2 class="sub-header">Section title</h2>
        <home-list :empList='empList' :delItem="delItem"></home-list>
      </div>`;
    
        window.AppHome = {
            template,
            components: {
                DashBoard,
                HomeList
            },
            data() {
                return {
                    hobbies: ['看书', '台球', '睡觉', '撸代码'],//dashboard显示数据
                    empList: [//homelist 显示数据
                        { id: 1, name: '小梦1', salary: 80001 },
                        { id: 2, name: '小梦2', salary: 80002 },
                        { id: 3, name: '小梦3', salary: 80003 },
                        { id: 4, name: '小梦4', salary: 80004 }
                    ]
                };
            },
            methods: {
                // 删除指定下标的数据
                // 因为删除 emp 会对 empList 做更新操作,
                // 而 empList 是初始化在当前这个组件里,所以删除的函数要定义在这个组件中
                delItem(index) {
                    this.empList.splice(index, 1);
                },
                deleteHobby(index) {
                    this.hobbies.splice(index, 1);
                }
            }
        }
    
    })()

    DashBoard.js

    ;(function(){
        const template=`
        <div class="row placeholders">
            <div v-for='(item,index) in hobbies' class="col-xs-6 col-sm-3 placeholder">
            <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200"
                height="200" class="img-responsive" alt="Generic placeholder thumbnail">
            <h4>{{item}}</h4>
            <span class="text-muted"><a @click='delHobby(index)' href="#">删除</a></span>
            </div>
        </div>
        `;
        window.DashBoard={
            template,
            props:['hobbies'] ,//定义父组件传过来的数据
            methods:{
                delHobby(index){
                    // 触发父组件中 del-Hobby 事件进行删除操作
                    this.$emit('del-Hobby', index);
                }
            }
        }
    })()

    8.6.8 slot插槽

    作用: 主要用于父组件向子组件传递 标签+数据 , (而上面prop和自定事件只是传递数据)
    场景:一般是某个位置需要经常动态切换显示效果

    a.子组件定义插槽

    在子组件中定义插槽, 当父组件向指定插槽传递标签数据后, 插槽处就被渲染,否则插槽处不会被渲染.

    <div>
        <!-- name属性值指定唯一插槽名,父组件通过此名指定标签数据-->
        <slot name="aaa">不确定的标签结构 1</slot>
        <div>组件确定的标签结构</div>
        <slot name="bbb">不确定的标签结构 2</slot>
    </div>

    b.父组件传递标签数据

    <child>
        <!--slot属性值对应子组件中的插槽的name属性值-->
        <div slot="aaa">向 name=aaa 的插槽处插入此标签数据</div>
        <div slot="bbb">向 name=bbb 的插槽处插入此标签数据</div>
    </child>

    c.插槽注意事项

    1. 只能用于父组件向子组件传递 标签+数据

    2. 传递的插槽标签中的数据处理都需要定义所在父组件中

    8.6.9 slot插槽案例

     将Dashboard 的标题通过插槽显示

     将 Dashboard 的标题标签定义为插槽 <slot name="dashboard"></slot>

    ; (function () {
    
        const template = ` <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
    
        <!--右边上半区域-->
        <!--<h1 class="page-header">Dashboard</h1>-->
        <!--定义插槽-->
        <slot name="dashboard"></slot>
        <dash-board :hobbies='hobbies' @del-Hobby="deleteHobby"></dash-board>
    
        <!--右边下半区域-->
        <h2 class="sub-header">Section title</h2>
        <home-list :empList='empList' :delItem="delItem"></home-list>
      </div>`;
    
        window.AppHome = {
            template,
            components: {
                DashBoard,
                HomeList
            },
            data() {
                return {
                    hobbies: ['看书', '台球', '睡觉', '撸代码'],//dashboard显示数据
                    empList: [//homelist 显示数据
                        { id: 1, name: '小梦1', salary: 80001 },
                        { id: 2, name: '小梦2', salary: 80002 },
                        { id: 3, name: '小梦3', salary: 80003 },
                        { id: 4, name: '小梦4', salary: 80004 }
                    ]
                };
            },
            methods: {
                // 删除指定下标的数据
                // 因为删除 emp 会对 empList 做更新操作,
                // 而 empList 是初始化在当前这个组件里,所以删除的函数要定义在这个组件中
                delItem(index) {
                    this.empList.splice(index, 1);
                },
                deleteHobby(index) {
                    this.hobbies.splice(index, 1);
                }
            }
        }
    
    })()

    向子组件中定义的插槽处传递标签数据。其中标题使用 title 数据绑定

    ; (function () {
    
        const template = `
        <div>
            <!--头部导航区域-->
            <app-navbar></app-navbar>
    
            <!--核心区域:分左右两边-->
            <div class="container-fluid">
            <div class="row">
    
                <!--左边菜单栏区域-->
                <app-left></app-left>
    
                <!--右边主页面区域: 分上下两个区域-->
                <app-home>
                    <h1 slot="dashboard" class="page-header">{{title}}</h1>
                </app-home>
    
            </div>
            </div>
        </div>
        `;
    
        window.App = {
            template,
            components: {
                AppNavbar,
                AppLeft,
                AppHome
            },
            data() {
                return {
                    title: '仪表盘',
                };
            },
        }
    
    })()

    8.7 非父子组件间通信

    Vue.js 可通过 PubSubJS 库来实现非父子组件之间的通信 ,使用 PubSubJS 的消息发布与订阅模式,来进行数据的
    传递。

    理解:订阅信息 ==== 绑定事件监听 ,发布消息 ==== 触发事件。
    注意: 但是必须先执行订阅事件 subscribe ,然后才能 publish 发布事件。

    订阅消息(绑定事件监听)

    先在 created 钩子函数中订阅消息

    //event接收的是消息名称,data是接收发布时传递的数据
    PubSub.subscribe('消息名称(相当于事件名)', function(event, data) {
        //事件回调处理
    })

    发布消息(触发事件)

    PubSub.publish('消息名称(相当于事件名)', data)

    案例

    1.安装 pubsub-js

    npm install pubsub-js

    2.index.html 引入 pubsub-js 库

    <script src="./node_modules/vue/dist/vue.js"></script>
    <script src="./node_modules/pubsub-js/src/pubsub.js"></script>

    3.AppLeft.js 订阅消息

    初始化数量 ,在 created 钩子中订阅消息,监听当删除爱好后,进行统计已删除总数

    ; (function () {
    
      const template = `<div class="col-sm-3 col-md-2 sidebar">
        <ul class="nav nav-sidebar">
          <li class="active"><a href="#">Overview 
          <span v-show="delNum">({{ delNum }})</span></a>
          </li>
          <li><a href="#">Reports</a></li>
          <li><a href="#">Analytics</a></li>
          <li><a href="#">Export</a></li>
        </ul>
        <ul class="nav nav-sidebar">
          <li><a href="">Nav item</a></li>
          <li><a href="">Nav item again</a></li>
          <li><a href="">One more nav</a></li>
          <li><a href="">Another nav item</a></li>
          <li><a href="">More navigation</a></li>
        </ul>
        <ul class="nav nav-sidebar">
          <li><a href="">Nav item again</a></li>
          <li><a href="">One more nav</a></li>
          <li><a href="">Another nav item</a></li>
        </ul>
      </div>`;
    
      window.AppLeft = {
        template,
        data() {
          return {
            delNum: 0,
          };
        },
        created() {
          PubSub.subscribe('changeNum', (event, num) => { // 箭头函数
            // 删除成功
            this.delNum = this.delNum + num
          })
        }
      }
    
    })()

    4.AppHome.js 发布消息

    deleteHobby(index) {
                    this.hobbies.splice(index, 1);
                    // 删除成功,发布消息,导航统计数据
                    PubSub.publish('changeNum', 1)
                }

    优点:不管是父子之间还是非父子之间通信 PubSubJS 都可以实现

    8.8 单文件组件

    8.8.1 当前项目存在的问题

    **全局定义 (Global definitions) 强制要求每个 component 中的命名不得重复。
    **字符串模板 (String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 。
    **不支持 CSS (No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏。
    **没有构建步骤 (No build step) 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug
    (formerly Jade) 和 Babel。

    文件扩展名为 .vue 的 single-file components(单文件组件) 为以上所有问题提供了解决方法,并且还可以使用
    webpack 或 Browserify 等构建工具。

    8.8.2 单文件组件模板格式

    <template>
    // 组件的模块
    </template>
    <script>
    // 组件的JS
    </script>
    <style>
    // 组件的样式
    </style>
    You are never too old to set another goal or to dream a new dream!!!
  • 相关阅读:
    MyEclipse 使用快捷键
    修改MyEclipse默认的Servlet和jsp代码模板
    设置MyEclipse开发项目时使用的JDK
    65.广搜练习:细胞数目
    65.广搜练习:细胞数目
    61.新的开始(最小生成树)
    61.新的开始(最小生成树)
    66.广搜练习:最少关卡路
    66.广搜练习:最少关卡路
    64.广搜练习跳马问题
  • 原文地址:https://www.cnblogs.com/youguess/p/15466432.html
Copyright © 2011-2022 走看看