当前位置: 首页 > Web前端 > JavaScript

内部分享文章:Vue2源码分析

时间:2023-03-27 18:17:47 JavaScript

一、内容与形式1、介绍目录结构,找到核心入口2、介绍全局api3、带题互动阅读核心源码2、目录结构。circleci持续集成benchmarks性能评估dist输出目录示例caseflow流程声明文件packagesvuePackagescriptsengineeringsrc源代码目录test测试相关类型ts声明文件3、核心源代码目录├─compiler#编译相关逻辑│├─codegen│├─directives│└─parser├─core#vue核心代码│├─components#vue内置组件保活│├─global-api#vue全局api│├─instance#vue核心逻辑│├─observer#vue中的响应式原理│├─util│└─vdom#vue中的虚拟dom模块├─platforms#平台代码│├─web#weblogic-vue││├─compiler││├─runtime││├─server││└─util│└─weex#weexlogic-app│├─compiler│├─runtime│└─util├─server#服务端渲染模块├─sfc#用于编译.vue文件└─shared#Shared方法和常量4.打包过程及入口分析1.package.json"build":"nodescripts/build.js","build:ssr":"npmrunbuild--web-runtime-cjs,web-server-renderer","build:weex":"npmrunbuild--weex",2.scripts/build.js//1.获取不同打包配置letbuilds=require('./config').getAllBuilds()//2.打包build(builds)3.scripts/config.jsexports.getAllBuilds=()=>Object.keys(builds)。map(genConfig)//找到包入口src/platforms/web/entry-runtime.jssrc/platforms/web/entry-runtime-with-compiler.js4.核心代码,全局APIinitGlobalAPI()1.importVuefrom'./runtime/index'2.importVuefrom'core/index'3.importVuefrom'./instance/index'//global-api/index.jsVue.setVue.deleteVue.nextTickinitUse(Vue)Vue.useinitMixin(Vue)Vue.mixininitExtend(Vue)Vue.extend5.Vue.set/Vue.deleteexport函数集(target:Array|Object,key:any,val:any):any{//1.Array使用splice触发视图更新vue.set(arr,0,99)if(Array.isArray(target)&&isValidArrayIndex(key)){target.length=M.max(target.length,key)target.splice(key,1,val)returnval}//2.对象是对象本身的一个属性,直接加上即可if(keyintarget&&!(keyinObject.prototype)){target[key]=valreturnval}constob=(target:any).__ob__//3。如果不是响应式的,则不需要定义为响应式属性if(!ob){target[key]=valreturnval}//4.将属性定义为响应式defineReactive(ob.value,key,val)ob.dep.notify()returnval}exportfunctiondel(target:Array|Object,key:any){//1.数组仍然调用拼接方法if(Array.isArray(target)&&isValidArrayIndex(key)){target.splice(key,1)return}constob=(target:any).__ob__//2.本身没有这个属性if(!hasOwn(target,key)){return}//3.删除该属性deletetarget[key]if(!ob){return}//4.通知更新ob.dep.notify()}对象只会拦截现有属性变化数组索引,不会触发视图更新6.Vue.nextTickconst回调=[];//存储nextTick回调letpending=false;functionflushCallbacks(){//清除队列pending=falseconstcopies=callbacks.slice(0)callbacks.length=0for(leti=0;i{if(cb){try{cb.call(ctx)//1.将回调函数存储在回调中}catch(e){handleError(e,ctx,'nextTick')}}elseif(_resolve){_resolve(ctx)}})if(!pending){pending=truetimerFunc();//2.异步刷新队列}//3.支持promise写法if(!cb&&typeofPromise!=='undefined'){returnnewPromise(resolve=>{_resolve=resolve})}}存储回调函数在一个队列中,最后异步清空队列timerFunc//1.默认优先使用Promise,因为mutationObserver有bug,可能无法工作if(typeofPromise!=='undefined'&&isNative(Promise)){constp=Promise.resolve()timerFunc=()=>{p.then(flushCallbacks)//解决不刷新队列的问题if(isIOS)setTimeout(noop)}isUsingMicroTask=true//2.使用MutationObserver}elseif(!isIE&&typeofMutationObserver!=='undefined'&&(isNative(MutationObserver)||MutationObserver.toString()==='[objectMutationObserverConstructor]')){letcounter=1constobserver=newMutationObserver(flushCallbacks)const文字Node=document.createTextNode(String(counter))observer.observe(textNode,{characterData:true})timerFunc=()=>{counter=(counter+1)%2textNode.data=String(counter)}isUsingMicroTask=true//3.使用setImmediate}elseif(typeofsetImmediate!='undefined'&&isNative(setImmediate)){timerFunc=()=>{setImmediate(flushCallbacks)}//4.使用setTimeout}else{timerFunc=()=>{setTimeout(flushCallbacks,0)}}EventLoop微任务和宏任务,微任务优先级降级处理承诺=>MutationObserver=>setImmediate=>setTimeout5。初始化流程initMixin(Vue)//_initstateMixin(Vue)//$set$delete$watcheventsMixin(Vue)//$on$emit$once$offlifecycleMixin(Vue)//_updaterenderMixin(Vue)//_render//初始化init.jsinitLifecycle(vm)//初始化组件间的父子关系initEvents(vm)//更新组件事件initRender(vm)//初始化_c方法创建虚拟节点initInjections(vm)//解析data/props之前的注入//初始化injectinitState(vm)//初始化状态initProvide(vm)//resolveprovideafterdata/props/initializationprovide//1.如果有el,开始挂载if(vm.$options.el){vm.$mount(vm.$options.el)}//2.挂载的组件加载Vue.prototype.$mount=function(el,hydrating){el=el&&inBrowser?query(el):undefinedreturnmountComponent(this,el,hydrating);}//3.创建一个用于渲染的渲染观察器exportfunctionmountComponent(vm,el,hydrating){vm.$el=elletupdateComponentupdateComponent=()=>{vm._update(vm._render(),hydrating)}newWatcher(vm,updateComponent,noop,{//在()之前创建renderWatcher{if(vm._isMounted&&!vm._isDestroyed){callHook(vm,'beforeUpdate')}}},true/*isRenderWatcher*/)returnvm}6.数据劫持+问题?1、这个怎么访问数据和方法?//初始化数据functioninitData(vm){letdata=vm.$options.data;//实例的_data属性是传入的数据//vue组件数据推荐使用函数,防止组件间共享数据data=vm._data=typeofdata==="function"?data.call(vm):数据||{};//将data数据代理给vm,也就是vue实例,我们可以使用this.a来访问this._data.afor(letkeyindata){proxy(vm,`_data`,key);}}//数据代理函数proxy(object,sourceKey,key){Object.defineProperty(object,key,{get(){returnobject[sourceKey][key];},set(newValue){object[sourceKey][key]=newValue;},});}通过this直接访问data中的数据的原因是:data中的属性finally会被存储在newVue实例(vm)上的_data对象中,访问this.xxx是在Object.defineProperty代理之后访问this._data.xxx//initMethodsfunctioninitMethods(vm,methods){varprops=vm.$options.props;for(varkeyinmethods){vm[key]=typeofmethods[key]!=='function'?noop:绑定(方法[键],虚拟机);}}functionnativeBind(fn,ctx){returnfn.bind(ctx)}varbind=Function.prototype.bind?nativeBind:polyfillBind通过this直接访问methods中的函数,因为methods中的方法通过bind(vm)将this指定为newVue的一个实例2.响应式数组?1.因为拦截数组下标太浪费了,所以在Observer构造函数传入的数据参数中加入性能//src/obserber/index.jsimport{arrayMethods}from"./array";classObserver{constructor(value){如果(阿拉y.isArray(value)){//这里对数组进行额外的判断//通过重写数组原型方法value.__proto__=arrayMethods拦截数组的七个方法;//如果数组中还包含数组,则需要递归判断this.observeArray(value);}else{this.walk(值);}}observeArray(items){for(leti=0;iarrayMethods=>arrayMethods.__proto__=>Array.prototypeletmethodsToPatch=["push","pop","shift","unshift","splice","re??verse","sort",];methodsToPatch.forEach((method)=>{arrayMethods[method]=function(...args){//这里保存原型方法的执行结果constresult=arrayProto[method].apply(this,args);//这句话是key//this是检测到的数据,比如数据是{msg:[1,2,3]},msg.push(4)thisismsgobismsg.__ob__(上面的代码是添加的,响应式已经观察到数据了)所以可以用ob.observeArrayconstob=this.__ob__;//这里的符号表示数组有新的操作letinserted;switch(method){case"push":case"unshift":inserted=args;break;case"splice":inserted=args.slice(2);default:break;}//如果有新插入的元素是数组,调用observeArrayObserver实例的每个数组项的观察if(inserted)ob.observeArray(inser泰德);//之后我们也可以检测到数组发生变化,触发视图更新操作——后续源码会公布返回结果;};});7.Vue简易版https://github.com/minxiang51...8.总结源码并非遥不可及,源码学习思路,千里之行,始于足下!