vue源码剖析

一、首先要搭建调试环境

  • 获取地址:https://github.com/vuejs/vue    这里是下载到本地了,但是如果想上传到自己的github上需要再建一个文件夹,把除README.md之外的所有文件拷贝过去,然后就是本地库与github远程库建立关系了:https://blog.csdn.net/north1989/article/details/53471439
  • 安装依赖:npm i
  • 安装rollup: sudo npm i rollup -g
  • 修改packge.json:   在dev的脚本处添加:--sourcemap
  • 执行脚本:npm run dev   即可生成dist中的新的vue.js代码,并带有一个vue.js.map 

二、调试技巧

  • 打开指定文件:ctrl + p   在弹出的搜索框中输入文件名称查找文件,这样就可以很快的在source中找到并进行调试了。
  • 想看这个文件具体在项目中的哪个位置:右键--->reveal in sideBar  就可以看到了
  • 断点  
  • 单步调试:F10
  • 进入方法:F11
  • 查看调用栈: callStack  这个很有用,可以看程序都执行了哪几步,现在程序到哪了。

 

初始化过程:

一、找入口文件(entry-runtime-with-compiler.js) 作用:覆盖$mount,执行模板解析和编译工作

思路:

  • package.json的dev脚本中查找dev脚本
"scripts": {
    "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",   //查找-c scripts/config.js文件  然后进到scripts/config.js文件中,又由于TARGET:web-full-dev 这句,在scripts/config.js中搜索web-full-dev
  • 在web-full-dev这个方法中,再去找entry这个属性,这里又有一个resolve() ,再进到resolve这个方法中(cmd + 鼠标左键可以进到原方法处)

  • 进到resolve()方法后,显然还有一个aliases文件,再进到alias文件中:
const aliases = require('./alias')   // 进到alias这个文件
const resolve = p => {
  const base = p.split('/')[0]  //这里显然有一个base基地址,然后再去看一下alias这个文件
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))  
  } else {
    return path.resolve(__dirname, '../', p)
  }
}
  • 进到了alias中: 进而找到了打包vue的入口文件:entry-runtime-with-compiler.js
//alias.js文件
const path = require('path')

const resolve = p => path.resolve(__dirname, '../', p)

module.exports = {
  vue: resolve('src/platforms/web/entry-runtime-with-compiler'), 
  compiler: resolve('src/compiler'),
  core: resolve('src/core'),
  shared: resolve('src/shared'),
  web: resolve('src/platforms/web'), // 这里有一个web,显示这里就是scripts/config.js中的web的地址,然后和entry-runtime-with-compiler.js拼起来,入口文件的地址就是src/platforms/web/entry-runtime-with-compiler.js   
  weex: resolve('src/platforms/weex'),
  server: resolve('src/server'),
  sfc: resolve('src/sfc')
}

 

二、src/platforms/web/runtime/index.js    :创建$mount

三、定义全局API:  src/core/index.js    

src/core/index.js最重要的作用:定义了全局API

四、查找vue构造函数:src/core/instance/index.js    ********* 最重要 ******

initMixin(Vue)  //通过initMixin方法给vue添加_init方法
stateMixin(Vue)  //$set,$delete,$watch
eventsMixin(Vue)   //$emit,$on,$once,$off
lifecycleMixin(Vue)  //_update(),$forceUpdate(),$destroy()
renderMixin(Vue)    //$nextTick,_render()

五、src/core/instance/init.js   :初始化_init() 方法定义的地主,以后要经常过来研究

tips:   组件创建的顺序是自上而下的(没有爸爸怎么会有你),挂载的顺序是自下而上。

 

初始化流程总结:new Vue()构造函数 => this._init(options) => $mount  => mountComponent() => _render() => _update()

  • new Vue()调用 _init(options)  
  • _init(options)初始化各种属性
  • $mount调用mountComponent()
  • mountComponent()声明updateComponent() ,创建watcher()  一个组件一个watcher
  • _render() 获取虚拟DOM
  • _update()把虚拟DOM替换为真实DOM

数据响应式

一、入口文件:从src/core/instance/init.js中的initState方法中进到state.js   ====>   src/core/instance/state.js

initDate,获取data,设置代理,启动响应式observe

二、src/core/observer/index.js     

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor(value: any) {
    this.value = value
    //为什么要在observe中声明dep??? why?
    //1、object中新增或删除属性  2、Array中有变更方法(push,shift,unshift等七君子)  都去更新数据(用def)
    this.dep = new Dep()

 

调试:从init.js开始

 

三、src/core/observer/array.js    array的七君子

const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

 

vue2.0中追加属性的做法:只能用Vue.set()的方法

obj = {foo:'foo'}
obj.bar = 'aaa'   //error

Vue.set(obj,'bar','aaa')  //ok
 
//array
let items = [1,2,3]
Vue.set(items,0,'abc')  //ok

items.length = 0 //error
items[0] = 'aaa'   //error

 

vue2.0的缺点:

  • 每次更新都要循环,性能低
  • API不统一,数组、object两套方案,有时数据变了,界面不更新。

 

 

vue源码:dep(依赖收集)

宏任务:setTimeout()  mutations等等。宏任务会在浏览器进行刷新的时候才执行,所以它是最后执行。如下例子:

微任务:promise,DOM发生变化等

console.log('script start');

setTimeout(function(){
    console.log('setTimeout')    //setTimeout为宏任务,会在所有同步和异步任务执行完成后再执行
},0)

Promise.resolve.then(function(){
    console.log('promise1')   //异步任务,会在同步任务执行完成后执行
}).then(function(){
    console.log('promise2')
})

console.log('script end')

打印顺序为:
script start
script end
promise1
promise2
setTimeout

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值