Vue.extend
屬於Vue的全局 api,在實際業務開發中咱們不多使用,由於相比經常使用的 Vue.component
寫法使用 extend
步驟要更加繁瑣一些。可是在一些獨立組件開發場景中(例如:ElementUI庫),因此Vue.extend
+ $mount
這對組合很是有必要須要咱們瞭解下。javascript
文檔html
官網用法:vue
註冊或獲取全局組件。註冊還會自動使用給定的 id
設置組件的名稱java
// 註冊組件,傳入一個擴展過的構造器
Vue.component('my-component', Vue.extend({ /* ... */ }))
// 註冊組件,傳入一個選項對象 (自動調用 Vue.extend)
Vue.component('my-component', { /* ... */ })
// 獲取註冊的組件 (始終返回構造器)
var MyComponent = Vue.component('my-component')
let ElInput = Vue.component('ElInput');
console.log(new ElInput); // 就是Inout的實例
複製代碼
用法也特別的簡單,你寫好的組件,直接在main.js裏面導入而後使用Vue.component('xx-xxx',xxx)
就能夠全局通用了。node
好處
就是全部頁面基本上都是經過 router 來管理能夠直接註冊,組件的建立咱們不須要去關注,相比 extend
要更省心一點#app
下渲染,註冊組件都是在當前位置渲染。若是我要實現一個模態框的提示組件,就比較雞肋了。這時候,Vue.extend + vm.$mount
組合就派上用場了。web
文檔api
官網用法:瀏覽器
<div id="mount-point"></div>
複製代碼
// 建立構造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 建立 Profile 實例,並掛載到一個元素上。
new Profile().$mount('#mount-point')
複製代碼
結果以下:緩存
<p>Walter White aka Heisenberg</p>
複製代碼
能夠發現Id
爲mount-point
的div ,是直接被替代了,沒有在內部填充,在咱們 main.js
初始化的時候用發其實也是同樣:app
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app'); // 此處都是替換,不是填充
複製代碼
下面簡單的分析下$mount
的源碼
// src/platforms/web/entry-runtime-with-compiler.js
import config from 'core/config'
import {warn, cached} from 'core/util/index'
import {mark, measure} from 'core/util/perf'
import Vue from './runtime/index'
import {query} from './util/index'
import {compileToFunctions} from './compiler/index'
import {shouldDecodeNewlines, shouldDecodeNewlinesForHref} from './util/compat'
const idToTemplate = cached(id => {
const el = query(id)
return el && el.innerHTML
})
const mount = Vue.prototype.$mount; // 緩存了原型上的 $mount 方法
Vue.prototype.$mount = function (el, hydrating) {
el = el && query(el) // 經過 query函數 就是獲取真實的DOM元素
// 判斷獲取的真實 dom元素是否爲 body 或 documentElement 報警告
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(
`Do not mount Vue to <html> or <body> - mount to normal elements instead.`
)
return this
}
const options = this.$options
// 判斷有麼有render , 我們通常都是用 template 寫的vue,須要編譯
if (!options.render) {
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
)
}
}
} else if (template.nodeType) {
template = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
} else if (el) {
template = getOuterHTML(el) // 即獲取 el 的 outerHTML
}
if (template) {
// 而後經過compileToFunctions將 template 轉化爲 render 函數,options.render = render
// 全部 Vue 的組件的渲染最終都須要 render 方法,不管是用單頁面 .vue 方式開發,仍是寫了 el 或者 template 屬性,
// 最終都要被轉成 render 方法,那麼這個過程是 Vue 的一個 「在線編譯」 的過程, 它是調用 compileToFunctions 方法實現的,最後調用原型上的 $mount 方法掛載。
const {render, staticRenderFns} = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
}
}
// 若是沒有傳el的話,會直接執行這步,會把組件在內存中渲染完畢
return mount.call(this, el, hydrating)
}
// 及時獲取HTML 兼容IE
function getOuterHTML(el): string {
if (el.outerHTML) {
return el.outerHTML
} else {
const container = document.createElement('div')
container.appendChild(el.cloneNode(true))
return container.innerHTML
}
}
Vue.compile = compileToFunctions
export default Vue
複製代碼
$mount
方法支持傳入 2 個參數,第一個是 el,它表示掛載的元素,能夠是字符串,也能夠是 DOM 對象,若是是字符串在瀏覽器環境下會調用 query 方法轉換成 DOM 對象的。第二個參數是和服務端渲染相關,在瀏覽器環境下不須要傳第二個參數。
$mount
方法實際上會調用mountComponent
方法,方法定義在 src/core/instance/lifecycle.js
中
// message/src/index.vue
<template>
<div class="wrap">
<div class="message" :class="item.type" v-for="item in notices" :key="item._name">
<div class="content">{{item.content}}</div>
</div>
</div>
</template>
<script>
// 默認選項
const defaultOptions = {
duration: 1500,
type: 'info',
content: '這是一條提示信息!',
}
let mid = 0
export default {
name:'MyMessage', // 建議添加方便外面直接取值
data() {
return {
notices: []
}
},
methods: {
addMessage(notice = {}) {
// name標識 用於移除彈窗
let _name = this.getName()
// 合併選項
notice = Object.assign({
_name
}, defaultOptions, notice)
this.notices.push(notice)
setTimeout(() => {
this.removeNotice(_name)
}, notice.duration)
},
getName() {
return 'msg_' + (mid++) //建立一個惟一的值
},
removeNotice(_name) {
let index = this.notices.findIndex(item => item._name === _name)
this.notices.splice(index, 1) // 刪除當前超時的dom
}
}
}
</script>
<style scoped>
.wrap {
position: fixed;
top: 50px;
left: 50%;
display: flex;
flex-direction: column;
align-items: center;
transform: translateX(-50%);
}
.message {
--borderWidth: 3px;
min-width: 240px;
max-width: 500px;
margin-bottom: 10px;
border-radius: 3px;
box-shadow: 0 0 8px #ddd;
overflow: hidden;
}
.content {
padding: 8px;
line-height: 1.3;
}
// 對應的集中狀態
.message.info {
border-left: var(--borderWidth) solid #909399;
background: #F4F4F5;
}
.message.success {
border-left: var(--borderWidth) solid #67C23A;
background: #F0F9EB;
}
.message.error {
border-left: var(--borderWidth) solid #F56C6C;
background: #FEF0F0;
}
.message.warning {
border-left: var(--borderWidth) solid #E6A23C;
background: #FDF6EC;
}
</style>
複製代碼
// message/index.js
import Vue from 'vue'
import Message from './src/index.vue'
let messageInstance = null
let TempMessage = ''
// 模擬異步請求
setTimeout(() => {
TempMessage = Message
}, 2000)
let init = () => {
let MessageConstructor = TempMessage && Vue.extend(TempMessage)
messageInstance = new MessageConstructor({})// 構造函數能夠接傳值,data、methods.....
// console.log(messageInstance);
// $mount()不帶參數,會把組件在內存中渲染完畢
messageInstance.$mount()
// messageInstance.$el 拿到的就是組件對應的dom元素,能夠直接操做dom
document.body.appendChild(messageInstance.$el)
messageInstance.$el.style.zIndex = 9999
}
let caller = (options) => {
if (!messageInstance) {
// 進頁面初始化
init(options)
}
// addMessage 是組件內部聲明的方法,也能夠經過構造函數傳對應的方法
messageInstance.addMessage(options)
}
export default {
install(vue) {
vue.prototype.$mymessage = caller
}
}
複製代碼
// main.js
import Message from '@/components/Message/index.js'
Vue.use(Message)
複製代碼
使用
this.$mymessage({
type: 'warning',
content: '你好壞啊,我好喜歡',
duration: 6000
})
複製代碼