Let's VUE[2]

197 阅读7分钟

组件

全局注册组件

  • Vue.extend(选项对象)创建组件构造器
  • Vue.component( id, [definition] )注册组件
  • 使用组件
//扩展组件构造器
let Custom = Vue.extend({
  template: '<div>我是全局组件</div>'
})
//注册组件
Vue.component('custom-component', Custom)
//简写方式,自动调用 Vue.extend
Vue.component('custom-component', {
  template: '<div>我是全局组件</div>'
})
//使用组件
<div id="app">
  <custom-component></custom-component>
</div>

局部注册组件

  • 选项对象的components属性注册局部组件,只能在所注册的作用域模板中使用
{
  components:{
    组件id:实例构造器 | 对象
  }
}
new Vue({
  el:'#app',
  components: {
    'custom-component': {
      template: `<div>我是局部注册的组件</div>`
    }
  }
})

组件命名约定和模板

注册组件命名:

  • kebab-case (短横线分隔命名)
  • camelCase (驼峰式命名)
  • PascalCase (单词首字母大写命名)

使用组件命名:

  • kebab-case (短横线分隔命名)

父子组件之间的通信

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

Vue.component('parent', {
  data(){
    return {
      name: 'parent'
    }
  },
  template: `<div>
              {{name}}
              <child></child>
            </div>`,
  components: {
    child: {
      data(){
        return {
          name: 'child'
        }
      },
      template: `<div>{{name}}</div>`
    }
  }
})

new Vue({
  el: '#app'
})
  • 父组件传递参数
    1. 在child组件上添加属性,如果是动态属性用v-bind
    2. child组件使用props接受参数,可以验证参数

父组件传递参数

在child组件上绑定动态属性msg,把parent组件的msg值传给child组件

{
  data(){
    return {
      name: 'parent',
      msg: '你是谁'
    }
  },
  template: `<div>
              {{name}}
              <child :msg="msg"></child>
            </div>`,
  components: {
    child: {
      data(){
        return {
          name: 'child'
        }
      },
      template: `<div>{{name}}</div>`
    }
  }
}

child组件接受参数

{
  child: {
    props: ['msg'],
    data(){
      return {
        name: 'child'
      }
    },
    template: `<div>
      <p>我是:{{name}}</p>
      <p>{{msg}}</p>
    </div>`
  }
}

child参数验证

{
  props: {
    msg: {
      //类型
      type: String,
      //是否必须
      required: true,
      //默认值
      default: '',
      //验证
      validator(value) {
        return value
      }
    }
  }
}

完整示例

Vue.component('parent', {
  data(){
    return {
      name: 'parent',
      msg: '你是谁'
    }
  },
  template: `<div>
              {{name}}
              <child :msg="msg"></child>
            </div>`,
  components: {
    child: {
      props: {
        msg: {
          type: String,
          required: true,
          default: ''
        }
      },
      data(){
        return {
          name: 'child'
        }
      },
      template: `<div>
                  <p>我是:{{name}}</p>
                  <p>{{msg}}</p>
                </div>`
    }
  }
})
  • 子组件发布事件

    1. child组件使用$emit发布事件
    2. 在parent组件监听事件

子组件发布事件

Vue.component('parent', {
  ...
  template: `<div>
              {{name}}
              <child @change-msg="changeMsgHandle" :msg="msg"></child>
            </div>`,
  methods: {
    changeMsgHandle(val){
      this.msg = val;
    }
  },
  components: {
    child: {
      ...
      methods: {
        changeMsg(){
          this.$emit('change-msg','我是child组件');
        }
      },
      template: `<div>
                  <p>我是:{{name}}</p>
                  <p>{{msg}}</p>
                  <button @click="changeMsg" type="button">改变msg</button>
                </div>`
    }
  }
})

完整示例

Vue.component('parent', {
  data(){
    return {
      name: 'parent',
      msg: '你是谁'
    }
  },
  template: `<div>
              {{name}}
              <child @change-msg="changeMsgHandle" :msg="msg"></child>
            </div>`,
  methods: {
    changeMsgHandle(val){
      this.msg = val;
    }
  },
  components: {
    child: {
      props: {
        msg: {
          type: String,
          required: true,
          default: ''
        }
      },
      data(){
        return {
          name: 'child'
        }
      },
      methods: {
        changeMsg(){
          this.$emit('change-msg','我是child组件');
        }
      },
      template: `<div>
                  <p>我是:{{name}}</p>
                  <p>{{msg}}</p>
                  <button @click="changeMsg" type="button">改变msg</button>
                </div>`
    }
  }
})

使用插槽分发内容

  • 编译作用域

    • 父组件模板的内容在父组件作用域内编译;
    • 子组件模板的内容在子组件作用域内编译。
  • 插槽的作用

    • 将父组件中写在子组件一对标签内的结构混合在子组件模板中,这个过程称之为内容分发。使用特殊的 元素作为原始内容的插槽
  • 单个插槽

    • 如果子组件中没有一对slot标签,写在子组件标签对的内容会被丢弃 子组件中有slot标签,子组件标签对的内容会整体替换在slot标签位置 slot标签内的内容被视作备用内容
  • 具名插槽

    • 可以使用name来配置如何分发内容
    • 没有name的slot被视为默认插槽

插槽

组件的双向绑定

v-modal作用在组件上,建立双向绑定

  • 接收value这个prop
  • 有值变化时,发布input事件
<div id="app">
  <custom v-model="message"></custom>
  <!--v-model就是下面写法的语法糖-->
  <custom :value="message" @input="value=>message=value"></custom>
</div>

Vue.component('custom', {
  props:{
    value:{
      type: String
    }
  },
  template: `<div>
              <h2>{{value}}</h2>
              <button @click="changeTitle">改变</button>
            </div>`,
  methods: {
    changeTitle(){
      this.$emit('input','子组件改了')
    }
  }
});

new Vue({
  el: '#app',
  data:{
    message: '父级的数据'
  }
});
  • v-model定制 prop 和 event
<div id="app">
  <custom v-model="message" value='hello'></custom>
</div>

Vue.component('custom', {
  model: {
    prop: 'propName',
    event: 'eventName'
  },
  props:{
    propName: {
      type: String
    },
    value:{
      type: String
    }
  },
  template: `<div>
              <h2>{{value}}</h2>
              <h2>{{propName}}</h2>
              <button @click="changeTitle">改变</button>
            </div>`,
  methods: {
    changeTitle(){
      this.$emit('eventName','子组件改了')
    }
  }
});

new Vue({
  el: '#app',
  data:{
    message: '父级的数据'
  }
});

.sync 修饰符

  • 语法糖,会扩展成一个更新父组件绑定值的 v-on 侦听器
<div id="app">
  <custom :title.sync="message"></custom>
</div>

Vue.component('custom', {
  props:{
    title: {
      type: String,
      default: 'hello'
    }
  },
  template: `<div>
              <h2>{{title}}</h2>
              <button @click="changeTitle">改变</button>
            </div>`,
  methods: {
    changeTitle(){
      this.$emit('update:title',"改变了")
    }
  }
});

new Vue({
  el:'#app',
  data:{
    message: 'vuejs'
  }
});

注册全局自定义指令

  • 钩子函数

    • bind:只调用一次,指令第一次绑定到元素时调用
    • inserted:被绑定元素插入父节点时调用
    • update:更新时调用
    • componentUpdated: 更新完毕调用
    • unbind:只调用一次,指令与元素解绑时调用。
Vue.directive('my-directive', {
  bind: function () {},
  inserted: function () {},
  update: function () {},
  componentUpdated: function () {},
  unbind: function () {}
})

封装model组件

  • 样式
<style>
p,h4{
  margin:0;
}
.modal{
  width: 500px;
  background-color: #fff;
  border: 1px solid rgba(0,0,0,.2);
  border-radius: 6px;
  box-shadow: 0 3px 9px rgba(0,0,0,.5);
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%,-50%);
  z-index: 111;
}
.modal-header {
  padding: 15px;
  border-bottom: 1px solid #e5e5e5;
}
.modal-content {
  padding: 20px;
}
.modal-footer {
  padding: 15px;
  text-align: right;
  border-top: 1px solid #e5e5e5;
}
.btn {
  padding: 5px 15px;

}
.mask {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: rgba(55,55,55,.6);
  height: 100%;
  z-index: 100;
}
</style>
  • 结构
<div id="app">
  <Modal></Modal>
</div>
  • modal主体结构
<div class="modal">
  <div class="modal-header"></div>
  <div class="modal-content"></div>
  <div class="modal-footer"></div>
</div>
  • modal的头部,内容,底部都可能定制内容
  • 每部分都添加一个slot
  • 点击按钮需要发布事件
<div class="modal-header">
  <slot name='header'>
    <h4>{{title}}</h4>
  </slot>
</div>

<div class="modal-content">
  <slot name="content">在这里添加内容</slot>
</div>

<div class="modal-footer">
  <slot name="footer">
    <input
      @click="okHandle"
      class="btn"
      type="button"
      :value="okValue"  
    />
    <input
      @click="cancelHandle"
      class="btn"
      type="button"
      :value="cancelValue"
    />
  </slot>
</div>
  • 组件完整结构
<script type="text/x-template" id="modal-temp">
  <div v-transform-body v-show="value" class="modal-example">
    <div class="mask"></div>
    <div class="modal">
      <div class="modal-header">
        <slot name='header'>
          <h4>{{title}}</h4>
        </slot>
      </div>
      <div class="modal-content">
        <slot name="content">在这里添加内容</slot>
      </div>
      <div class="modal-footer">
        <slot name="footer">
          <input
            @click="okHandle"
            class="btn"
            type="button"
            :value="okValue"  
          />
          <input
            @click="cancelHandle"
            class="btn"
            type="button"
            :value="cancelValue"
          />
        </slot>
      </div>
    </div>
  </div>
</script>
  • 注册Modal组件
  • 使用props接受参数,并验证
  • 添加监听事件
Vue.component('Modal',{
  props:{
    value:{
      type: Boolean,
      default: false
    },
    title:{
      type: String,
      default: '默认标题'
    },
    okValue:{
      type: String,
      default: '确定'
    },
    cancelValue:{
      type: String,
      default: '取消'
    }
  },
  template: '#modal-temp',
  methods: {
    okHandle () {
      this.$emit('ok-click');
      this.$emit('input',false);
    },
    cancelHandle () {
      this.$emit('cancel-click');
      this.$emit('input',false);
    }
  }
})
  • 自定义插入节点到body指令
Vue.directive('transform-body',{
  inserted(el){
    document.body.appendChild(el);
  }
});
<div id="app">
  <button @click="showModal" type="button">显示modal</button>
  <Modal v-model="show" @ok-click="okHandle" @cancel-click="cancelHandle"></Modal>
</div>

new Vue({
  el:'#app',
  data:{
    show: true
  },
  methods: {
    showModal(){
      this.show = true;
    },
    okHandle(){
      console.log('点击了确定按钮')
    },
    cancelHandle(){
      console.log('点击了取消按钮')
    }
  }
});

生命周期函数

  • beforeCreate:数据劫持之前被调用,无法访问methods,data,computed上的方法或数据

  • created:实例已经创建完成之后被调用。但挂载阶段还没开始,$el 属性目前不可见。常用于ajax发送请求获取数据

  • beforeMounted:在挂载开始之前被调用

  • mounted:vue实例已经挂载到页面中,可以获取到el中的DOM元素,进行DOM操作

  • beforeUpdated:更新数据之前调用

  • updated:组件 DOM 已经更新

  • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。

  • destroyed:Vue 实例销毁后调用

  • activated:keep-alive 组件激活时调用

  • deactivated:keep-alive 组件停用时调用

限制元素&动态组件

is

Vue 只有在浏览器解析、规范化模板之后才能获取其内容。
像 <ul>、<ol>、<table>、<select> 这样的元素里允许包含的元素有限制,而另一些像 <option> 这样的元素只能出现在某些特定元素的内部。
<div id="app">
  <table>
    <custom></custom>
  </table>

  <table>
    <tr is='custom'></tr>
  </table>
</div>

Vue.component('custom',{
  template: `<tr><td>hello</td></tr>`
});

new Vue({
  el: '#app'
});
  • 通过使用保留的 <component> 元素,并对其 is 特性进行动态绑定
var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})

<component v-bind:is="currentView">
  <!-- 组件在 vm.currentview 变化时改变! -->
</component>

keep-alive

  • 把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染
  • include - 字符串或正则表达式。只有匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。任何匹配的组件都不会被缓存
<!-- 基本 -->
<keep-alive>
  <component :is="view"></component>
</keep-alive>

<!-- 多个条件判断的子组件 -->
<keep-alive>
  <comp-a v-if="a > 1"></comp-a>
  <comp-b v-else></comp-b>
</keep-alive>

<!-- 和 `<transition>` 一起使用 -->
<transition>
  <keep-alive>
    <component :is="view"></component>
  </keep-alive>
</transition>