Vue.extend( options ) : 基础Vue构造器
参数是一个包含组件选项的对象。
data选项是特例,在Vue.extend()中它必须是函数,为了保证引用数据不乱来。
<div id='app'>
<span>MyApp</span>
</div>
<script>
var Profile = Vue.extend({
template: '<p>目标:绑定到id为app的元素下面 打印data中的数据{{p1}},{{p2}}</p>',
data:function(){
return {
p1:'打印记录1',
p2:'打印记录2'
}
}
})
//进行绑定
new Profile().$mount('#mount-point');
</script>
为什么要使用extend ?
常规办法,我们的所有页面都是用router进行管理,组件也是通过import进行局部注册,也会存在不足的地方。
例如:假设我们需要从接口动态渲染组件的情况?如何实现一个类似window.alert()类似的提示组件,要求调用js一样使用?
如何使用extend构造一个自定义弹窗
1.编写弹窗组件
// 测试的话 建议直接复制粘贴
<template>
<div v-if="show" ref="modal" class="ek-modal_wrap">
<div class="ek-modal-content">
<div class="modal-title-wrap">
<div class="modal-title">{{ title }}</div>
<slot name="description"></slot>
</div>
<div class="modal-button">
<a v-if="confirmVisible" class="contral-btn confirm-btn" href="javascript:;" @click="confirm">{{
confirmText
}}</a>
<a v-if="cancleVisible" class="contral-btn cancle-btn" href="javascript:;" @click="cancle">{{ cancleText }}</a>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
show: true,
title: '', // 标题
confirmText: '确定', // 确认文字
confirmVisible: true, // 是否展示确认按钮
onConfirm: () => { // 确认执行函数
this.$emit('confirm')
},
cancleText: '取消', // 取消文字
cancleVisible: true, // 是否展示取消按钮
onCancle: () => { // 取消执行函数
this.$emit('cancle')
}
}
},
methods: {
confirm() {
this.onConfirm()
this.close()
},
cancle() {
this.onCancle()
this.close()
},
close() {
this.show= false
if (this.$refs.modal) {
this.$refs.modal.remove() // 关闭时候直接移除当前元素
}
}
}
}
</script>
<style lang="scss" scoped>
.ek-modal_wrap {
position: fixed;
top: 0;
left: 0;
z-index: 999;
width: 100%;
height: 100%;
font-size: 28px;
background: rgba(0, 0, 0, 0.7);
.ek-modal-content {
position: fixed;
top: 50%;
left: 50%;
min-width: 7.2rem;
overflow-x: hidden;
overflow-y: hidden;
text-align: center;
background-color: white;
border-top-left-radius: 0.266667rem;
border-top-right-radius: 0.266667rem;
border-bottom-right-radius: 0.266667rem;
border-bottom-left-radius: 0.266667rem;
transform: translate(-50%, -50%);
.modal-title-wrap {
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: center;
min-height: 55px;
padding: 0 20px;
color: #333;
}
.modal-title {
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: center;
min-height: 100px;
margin-top: 30px;
margin-bottom: 30px;
font-weight: 600px;
line-height: 50px;
color: #333;
}
.modal-button {
display: flex;
line-height: 1;
color: #333;
border-top-color: #e7e7e7;
border-top-style: solid;
border-top-width: 1px;
& > a {
color: #333;
}
.contral-btn {
flex-basis: 0%;
flex-grow: 1;
flex-shrink: 1;
font-weight: 600px;
line-height: 3;
text-align: center;
&.cancle-btn {
border-left-color: #e7e7e7;
border-left-style: solid;
border-left-width: 1px;
}
}
}
}
}
</style>
第二步:在vue的原型上绑定方法。
import Vue from 'vue';
import dialog from './components/Dialog.vue';
function showDialog(options) {
const Dialog = Vue.extend(dialog); // 返回一个vue子类
//创建实例并且挂载
const app = new Dialog().$mount(document.createElement('div'));
//初始化参数
for (let key in options) {
app[key] = options[key];
}
//将元素插入body中
document.body.appendChild(app.$el);
}
Vue.prototype.$showDialog = showDialog; //将方法放在原型上。
第三步:在vue页面中调用方法既可
....某vue页面
mounted() {
console.log(this.$showDialog);
this.$showDialog({
title: '测试弹窗',
confirmText: '希望弹出窗口正确调用',
cancelVisible: false,
});
},
... 欢迎各位尝试体验
Vue.extend原理解析:
作用:Vue.extend返回的是一个扩展实例构造器,也就是预设了部分选项的Vue实例构造器,但未曾实例化,可以理解为创建一个子类,然后让它继承Vue身上的一些功能。
创建一个Vue的子类。
let cid = 1; // 用以进行特殊标记
function extend(extendOptions){
extendOptions = extendOptions || {}
// this->vue
const Super = this
const SuperId = Super.cid
// 使用父类的id作为缓存的key
// 只要父类id相同,每次调用会返回相同的子类
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
//如果存在缓存,可直接返回。 为了性能考虑,增加缓存策略
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
// 对name命名方式进行校验
// /^[a-zA-Z][\w-]*/
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
const Sub = function VueComponent (options) {
// vue._init 此函数的解析会在下面有解析
this._init(options)
}
// 继承父类构造函数
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
// 合并父类的options
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
// 初始化props
// 将props挂载在原型对象的_props属性下
if (Sub.options.props) {
initProps(Sub)
}
// 初始化computed
// 将初始化computed挂载在原型对象下
if (Sub.options.computed) {
initComputed(Sub)
}
// allow further extension/mixin/plugin usage
// 复制父类的静态方法
// 子类继承Vue的能力
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
}
1、为了性能考虑,在Vue.extend方法内增加了缓存策略。反复调用Vue.extend其实应该返回同一个结果。
2、只要返回结果是固定的,就可以将计算结果缓存,再次调用extend方法时,只需要从缓存中取出结果即可。
3、使用父类的id作为缓存的key,将子类缓存在cachedCtors中。
4、对name的校验,如果发现name选项不合格,会在开发环境下发出警告。
5、最后,创建子类并将它返回,这一步并没有继承的逻辑,此时子类是不能用的,它还不具备Vue的能力。
创建vue子类中 _init()函数解析
在上述代码中出现了一个vue的_init函数,附上其解析。简化版本
let uid = 0;
export function initMixin(Vue: Class<Component>) {
Vue.prototype._init = function(options?: Object) {
const vm: Component = this;
vm._uid = uid++; // 当前实例的 _uid 加 1
// a flag to avoid this being observed
// 用 _isVue 来标识当前实例是 Vue 实例, 这样做是为了后续被 observed
vm._isVue = true;
// merge options 合并options
if (options && options._isComponent) { // _isComponent 标识当前为 内部Component
// 内部Component 的 options 初始化
initInternalComponent(vm, options);
}
else { // 非内部Component的 options 初始化
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
}
// 在render中将this指向vm._renderProxy
if (process.env.NODE_ENV !== 'production') {
initProxy(vm);
}
else {
vm._renderProxy = vm;
}
// expose real self
vm._self = vm;
initLifecycle(vm); // 初始化生命周期
initEvents(vm); // 初始化事件
initRender(vm); // 初始化渲染函数
callHook(vm, 'beforeCreate'); // 回调 beforeCreate 钩子函数
initInjections(vm); // resolve injections before data/props
// 初始化 vm 的状态
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created'); // vm 已经创建好 回调 created 钩子函数
if (vm.$options.el) { // 挂载实例
vm.$mount(vm.$options.el);
}
}