一、首先要搭建调试环境
- 获取地址: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