創建組件的時候都會調用 Vue.extend
注冊組件第二個參數默認會調用extend,Vue.extend 使用Vue基礎構造器 產生子類
Vue.extend() 中data必須是一個函數,繼承與Vue,可以new和掛載
Vue.component("",Vue.extend({})) //傳入的是對象
- 子類可以繼承父類 init
- 每個組件都有自己的數據,希望每個組件都是獨立的所以希望new創造一個實例
為啥data必須是函數,而不是對象
class Component{
constructor(){
this.data = obj;
}
}
new Component().data
new Component().data
這樣復用了同一個對象所以改寫為,這樣new的時候可以拿到全新的對象
let fn = function(){
return {
}
}
class Component{
constructor(){
this.data = fn;
}
}
重寫Sub.prototype.constructor 指向(源碼中有寫)
- Sub.prototype = Object.create(Super.prototype);
- Object.setPrototypeOf()
- Object.create 原理
function create(parentProtptype){
const Fn = function(){}
Fn.prototype = parentProtptype
return new Fn;
}
Sun.prototype = Object.create(Super.prototype);//繼承原型方法
Sub.prototype.constructor = Sub;//Object.create 會產生一個新的實例作為子類的原型,此時constructor會指向錯誤
因為new Fn的constructor指向的是Parent,所以Sub.prototype需要重寫
———————————————— 分割線 ————————————————————
組件初始化流程
1:調用initGlobalAPI里邊有2個重要的方法
- Vue.extend:產生Vue的子類(實例),其中mergeOptions合并了全局屬性和子組件的屬性到,組件實例的options上
- Vue.component:將產生的組件是放到 Vue.options.components[組件名稱]全局上
export function initGlobalAPI(Vue) {
Vue.options = {};//全局屬性 每個組件初始化的時候 將這些屬性放到每個組件桑
Vue.mixin = function(options){
this.options = mergeOptions(this.options,options);
console.log(this.options);
return this;
}
//vue.component -> Vue.extend
Vue.options._base = Vue;
//等會我通知Vue.extend方法可以產生一個子類 new子類的時候會執行代碼初始化流程(組件的初始化)
Vue.extend = function (opt) {//會產生一個子類
const Super = this;
const Sub = function (options) {//創造一個組件 其實就是new這個組件的類(組件的初始化)
this._init(options)
}
Sub.prototype = Object.create(Super.prototype);//繼承原型方法
Sub.prototype.constructor = Sub;//Object.create 會產生一個新的實例作為子類的原型,此時constructior會指向錯誤 constructor就這個問題
//父類的屬性要和子類的屬性進行合并
Sub.options = mergeOptions(Super.options,opt);//需要讓子類 能拿到我們Vue定義的全局組件
//Sub.mixin = Vue.mixin; nextTixk等等
return Sub;//產生Vue的子類
}
Vue.options.components = {};//存放全局組件的
Vue.component = function name(id, definition) {//definition 可以傳入對象或者函數
let name = definition.name || id;
definition.name = name;
if(isObject(definition)){
definition = Vue.extend(definition);
}
Vue.options.components[name] = definition;//緩存組件到全局上(維護關系)
console.log(Vue.options.components);
}
}
在renderMixin的時候會調用
Vue.prototype._c = function () { // createElement 創建元素型的節點
const vm = this;
return createElement(vm, ...arguments)
}
所以在createElement,需要對組件進行處理(要區分組件和普通元素去創建虛擬節點)
function createComponent(vm,tag,data,children,key,Ctor) {
if(isObject(Ctor)){//組件的定義一定是通過Vue.extend進行包裹的
Ctor = vm.$options_base.extend(Ctor)
}
//組件的hook 源碼里是進行遍歷掛載hook
data.hook = {
//組件的生命周期
init(){
},
//組件的更新流程
prepatch(){
},
//......
}
//每個組件 默認的名字內部都會給你拼接一下 vue-component-1-my-button(數組是一個序號 每new一個+1)
let componentOptions = vnode(vm,tag,data,undefined,key,undefined,{Ctor,children,tag,propsData,listeners});
return componentOptions;//componentOptions(非常重要包含了Ctor children等) 存放了一個重要的屬性Ctor
}
//tag 有可能是組件 createElement({}) createElement(function(){})
export function createElement(vm, tag, data = {}, ...children) { // 返回虛擬節點 _c('',{}....)
//這個時候是虛擬節點 還沒有nodeType; 這里不考慮復雜的就是string類型 看看是不是普通標簽
if (!isReservedTag(tag)) {
//Ctor 是組件最重要的屬性
let Ctor = vm.$options.components[tag]; // 組件的初始化 就是new 組件的構造函數 tag標簽
//創建組件的虛擬節點 如果是組件要拿到組件的定義
return createComponent(vm, tag, data, children, data.key, Ctor);
}
return vnode(vm, tag, data, children, data.key, undefined)
}
export function createText(vm, text) { // 返回虛擬節點
return vnode(vm, undefined, undefined, undefined, undefined, text)
}
//options 可能是對象(自己定義的組件) 所以不放Ctor
function vnode(vm,tag,data,children,key,text,options) {
return {
vm, tag, data, children,key,text,componentOptions:options
}
}
判斷是不是原始的標簽 還是組件的方法
function makeMap(str) {
let tagList = str.split(',');
return function (tagName) {
return tagList.includes(tagName)
}
}
//這里的源碼在platform/web/util/element.js
export const isReservedTag = makeMap(
'html,body,base,head,link,meta,style,title,' +
'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
'embed,object,param,source,canvas,script,noscript,del,ins,' +
'caption,col,colgroup,table,thead,tbody,td,th,tr,' +
'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
'output,progress,select,textarea,' +
'details,dialog,menu,menuitem,summary,' +
'content,element,shadow,template,blockquote,iframe,tfoot'
)