当前位置: 首页 > Web前端 > vue.js

vue原理解析2:vue初始化流程与数据响应式梳理_0

时间:2023-03-31 23:29:49 vue.js

https://www.processon.com/vie...https://www.processon.com/vie...目标掌握源码学习方法vue初始化原理解剖深入理解数据响应式配置环境复制代码:gitclonehttps://github.com/vuejs/vue.gitnpminstall安装打包工具rollupnpmi-grollup修改package.json中dev的打包配置"dev":"rollup-w-cscripts/config.js--sourcemap--environmentTARGET:web-full-dev",执行脚本npmrundev在examples文件夹下添加测试文件文件目录vue├──dist#发布目录├──examples#Example,测试代码在这里├──flow#测试代码├──packages#核心代码之外的独立库├──scripts#构建脚本├──src#源代码├──test#ts类型声明,上面flow└──types#flowsrc的类型声明#源码├──编译器#编辑器相关├──core#核心代码├──────components#keep-alive等通用组件├──────global-api#全局API├──────────instance#构造functions等├────────observer#响应式相关├──────vdom#虚拟DOM相关└──────platforms#平台独有代码代码扩展vue源码源码分析-初始化流程入口的dev脚本中的scripts/config.js配置文件web-full-dev表示执行时的配置项'web-full-dev':{//entryentry:resolve('web/entry-runtime-with-compiler.js'),//exportdest:resolve('dist/vue.js'),//格式format:'umd',env:'development',alias:{he:'./entity-decoder'},banner},入口文件路径src/platforms/web/entry-runtime-with-compiler.js先extends$mount方法,处理template或eloption//extends$mountconstmount=Vue.prototype.$mountVue.prototype.$mount=function(el?:string|Element,hydrating?:boolean):组件{el=el&&query(el)if(el===document.body||el===document.documentElement){process.env.NODE_ENV!=='production'&&warn(`不要将Vue挂载到或-而是挂载到普通元素。`)returnthis}//userConfigurationoptionsconstoptions=this.$options//首先判断render是否存在if(!options.render){//获取模板选项lettemplate=options.template//判断模板是否存在,如果模板不存在,判断el是否存在if(template){}elseif(el){template=getOuterHTML(el)}if(template){//获取HTML模板字符后执行编译过程}}//执行mountreturnmount.call(this,el,hydrating)}Vue.prototype.$mount源码路径:src/platforms/web/runtime/index.js//实现补丁函数主要用于初始化和更新Vue.prototype.__patch__=inBrowser?patch:noop//实现了$mountVue.prototype.$mount=function(el?:string|Element,hydrating?:boolean):Component{el=el&&inBrowser?query(el):undefined//定义$mount挂载组件,将vnode转换为nodereturnmountComponent(this,el,hydrating)}查找vue路径:src/core/index.js//初始化component/fitter等全局API/directive/使用/mixin/util/extendinitGlobalAPI(Vue)找到Vue导入路径:src/core/instance/index.js声明Vue构造函数Vue(options){if(process.env.NODE_ENV!=='production'&&!(thisinstanceofVue)){warn('Vue是一个构造函数,应该使用`new`关键字调用')}//初始化方法this._init(options)}//instance属性实例方法initMixin(Vue)//与_init()方法混合stateMixin(Vue)//$data/$props/$set()$delete()$watch()eventsMixin(Vue)//$emit()/$on()$off()$once()事件相关方法lifecycleMixin(Vue)//_update/$forceUpdate/$destory生命周期相关方法renderMixin(Vue)//$nextTick()/_render()//渲染相关的initMixin方法路径:src/core/instance/init.js//实现一个_init方法的目的_init是供内部使用Vue.prototype._init=function(options?:Object){//做optionmergewill传入的选项与vue的默认选项合并if(options&&options._isComponent){}else{vm.$options=mergeOptions(resolveConstructorOptions(vm.constructor),options||{},vm)}//初始化核心代码vm._self=vminitLifecycle(vm)//$parent$root$children$refsinitEvents(vm)//自定义事件监听器initRender(vm)//$slots/$createElement定义了$attrs和$listenersresponsive//上面的方法进行了初始化准备beforeCreatecallHook(vm,'beforeCreate')//dispatchbeforeCreate,在beforeCreate中可以访问上面三个内容initInjections(vm)//resolveinjectionsbeforedata/propsinitState(vm)//props/methods/data/computed/watch数据初始化initProvide(vm)//resolveprovideafterdata/propscallHook(vm,'created')//当设置el选项时,$mount会自动调用if(vm.$options.el){vm.$mount(vm.$options.el)}}整体流程流程:newVue()=>创建vue实例_init()=>初始化数据、属性、事件$mount()=>执行挂载,将虚拟dom转化为真实的dommountComponent()=>将虚拟dom转化为真实的domnewWatcher()=>创建组件渲染watcherupdateComponent()=>执行初始化或更新_update()=>初始化或更新,将虚拟dom转换为真实dom_render()渲染组件,获取vdom~~~~Vue源码分析资料响应式vue2实现二-方式绑定,利用js对象的Object.defineProperty()拦截数据的setter/getter方法,结合发布订阅模式,在getter中订阅。在setter中发布通知,让所有订阅者完成响应的具体实现是,在Vue初始化时,会调用initState(vm)方法initState(vm)路径:src/core/instance/state.jsexportfunctioninitState(vm:Component){.....//判断数据是否存在if(opts.data){//如果数据存在,就到这里,因为依赖数据的双向绑定,所以找到initDatainitData(vm)}else{observe(vm._data={},true/*asRootData*/)}}initData(vm)functioninitData(vm:Component){....//传入的数据可能是一个函数或防止数据污染的对象;让data=vm.$options.datadata=vm._data=typeofdata==='function'?getData(数据,虚拟机):数据||{}....//代理,重复判断constkeys=Object.keys(data)while(i--){....//属性和方法不能重复判断if(process.env.NODE_ENV!=='production'){}if(props&&hasOwn(props,key)){}}}//数据响应式处理observe(data,true/*asRootData*/)}数据响应式处理路径:src/core/observer/index.jsexportfunctionobserve(value:any,asRootData:?boolean):Observer|void{....//为传入的对象值创建观察者实例;任何对象都伴随有一个观察者实例//返回一个观察者实例//使用观察者实例判断类型和内部响应处理//如果_ob_存在,直接返回if(hasOwn(value,'__ob__')&&value.__ob__instanceofObserver){ob=value.__ob__}elseif(...){...}else{...//如果没有处理,创建一个新的ob实例instanceob=newObserver(value)}}newObserver(value)path:src/core/observer/index.jsexportclassObserver{constructor(value:any){//可以理解为一个小管家:比如data中的数据就是object封装对象的存在,data:{obj:{foo:foo}},forobjects,是针对对象的新增和删除的通知工作;由小管家通知。//当你需要修改obj中的属性时,就发生在你的肚子里,所以你需要小管家处理//如果obj是数组,删除option也需要小管家//对象属性的变化或者数组元素的变化需要通知小管家更新this。dep=newDep()//小管家//判断传递if(Array.isArray(value)){//当是数组时处理//覆盖数组实例的原型}else{//处理一个对象this.walk(value)}}}walk(obj:Object){....constkeys=Object.keys(obj)//遍历所有当前keysfor(leti=0;i