Vue源码分析-之框架流程

原创
2017/02/15 09:26
阅读数 274

#Vue源码分析-之框架流程#

在github上找到了vue项目,并把它克隆到了本地

git clone https://github.com/vuejs/vue.git

然后我进入vue目录安装了下依赖项

npm install

###目录结构###

Vue目录结构

进入vue目录,先看看目录结构

benchmarks 测试目录
build 编译配置目录
dist 编译后目录
examples 实例目录
flow 定义全局变量类型
packages ?
src  源码目录
test 测试目录
types Typescript 类型定义文件目录
package.json npm配置文件

有很多Javascript的库,继承了一些 Javascript的环境变量以及语法, Typescript编译器并不能原生的支持这些。 所以我们使用 Typescript 类型定义文件 – d.ts 文件 (即 typings.json) 来解决这些兼容性问题。types目录就是干这个的

src目录 进入src目录直接看源码,看看src目录都有些啥

compiler 编译器目录
core   核心目录
entries  入口目录
platforms 平台目录
server   服务目录
sfc   解析.vue文件
shared  共享函数库目录

core目录

components 组件
global-api  全局api
instance  实例化Vue对象
observer  观察者
util   工具
vdom  dom相关
config.js  配置文件
index.js 入口文件	

###源码分析###

首先我们看看一个使用了vue的页面,新建dist/index.html文件,内容如下

<!DOCTYPE html>
<html>
<head>
    <title>vue</title>
</head>
<body>
    <div id="app">
        {{message}}
    </div>
    <script src="vue.js"></script>
    <script type="text/javascript">
    var app = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!'
        }
    })
    </script>
</body>
</html>

我们看到页面中分成了两块(模板和vue对象)

<div id="app">
    {{message}}
</div>

上面的模板定义了一个id为app的盒子,然后里面使用{{ }}来包含了一个变量,这个是个模板字符串,再看看下面的vue对象

var app = new Vue({
    el: '#app',
    data: {
        message: 'Hello Vue!'
    }
});

我们看到new了一个Vue构造函数,传入了一个json参数,这个json参数有两个属性

{
    el: '#app',
    data: {
        message: 'Hello Vue!'
    }
}

那么这个Vue构造函数定义在哪里呢,内部又有那些方法呢,我们来看看他的源码。

###流程结构###

首先我们来寻找框架的入口文件

打开根目录的package.json,查看运行命令,直接看scripts.dev

"scripts": {
	"dev": "TARGET=web-full-dev rollup -w -c build/config.js",
	......

当我们运行 npm run dev 命令,使用rollup启动打包构建程序,使用的配置文件是build/config.js,TARGET=web-full-dev。那我们打开build/config.js文件,找到相关的配置项

const builds = {
	...
	// Runtime+compiler development build (Browser)
	'web-full-dev': {
		entry: path.resolve(__dirname, '../src/entries/web-runtime-with-compiler.js'),
		dest: path.resolve(__dirname, '../dist/vue.js'),
		format: 'umd',
		env: 'development',
		alias: { he: './entity-decoder' },
		banner
	},
	...
}

我们看到一共有11中编译选项,而dev命令所运行的编译选项就是上面的web-full-dev,它的入口文件是src/entries/web-runtime-with-compiler.js,编译导出的文件是dist/vue.js

入口文件src/entries/web-runtime-with-compiler.js

打开src/entries/web-runtime-with-compiler.js

import Vue from './web-runtime'  //导入编译运行时

Vue.prototype.$mount = //编译运行时$mount修正

Vue.compile = ...  //编译入口

export default Vue  //导出编译运行时Vue

web运行时src/entries/web-runtime.js

打开src/entries/web-runtime.js

import Vue from 'core/index'  //导入Vue

// 核心配置扩展

Vue.config.isUnknownElement = isUnknownElement
Vue.config.isReservedTag = isReservedTag
Vue.config.getTagNamespace = getTagNamespace
Vue.config.mustUseProp = mustUseProp

// 安装平台运行时指令或与组件

extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// 安装平台补丁功能

Vue.prototype.__patch__ = inBrowser ? patch : noop

// 包装 mount

Vue.prototype.mount = function(){....}

//埋入dev全局钩子工具

setTimeout(()=>{
	if (config.devtools) {
    if (devtools) {
      devtools.emit('init', Vue)
      ...
},0)

//导出Vue

export default Vue

src/core/index.js

import Vue from './instance/index' //实例化Vue类
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'

initGlobalAPI(Vue) //安装vue_api接口到Vue

//当前 Vue 实例是否运行于服务器。
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})

Vue.version = '__VERSION__'  //版本号

export default Vue //导出Vue

我们看到这个文件首先设置了vue_api接口到Vue,然后设置vm.$isServer变量值,然后设置了版本号,最后导出了Vue

先从第一行开始看起 import Vue from './instance/index'

我们打开instance目录

render-helpers  一些模版渲染的辅助函数
events.js Vue对象的一些事件扩展
index.js  入口文件
init.js  扩展Vue对象 _init、vm.$options
lifecycle.js  扩展Vue对象生命周期 
proxy.js  代理,运行时提示一些不存在的变量或方法
render.js 扩展Vue对象的render、nextTick
state.js vue状态维护,检测data变化等

src/core/instance/index.js

import { initMixin } from './init'  
import { stateMixin } from './state' 
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

//这就是Vue的构造函数
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options) //初始化选项
}

initMixin(Vue)//给Vue函数挂载_init方法
stateMixin(Vue) //给Vue实例挂载观察者的数据对象。Vue 实例代理了对其 data 对象属性的访问。
eventsMixin(Vue) //给Vue实例挂载事件处理方法
lifecycleMixin(Vue) //给Vue实例挂载生命周期钩子
renderMixin(Vue) //给Vue实例挂载模板渲染方法

export default Vue

至此整个流程走完,在这个文件里面定义了Vue构造函数,并对Vue构造函数进行了扩展,依次向上导出Vue,在上层对Vue进行了各种扩展。

相关资料

http://www.kancloud.cn/zmwtp/vue2/150241

后面我会对单个功能进行详细分解,敬请期待

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部