Let's VUE[2]

webdq發表於2018-05-02

元件

全域性註冊元件

  • 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>
複製程式碼

相關文章