Vue2源码☞ 1 ☞ 搭建源码学习环境

762 阅读2分钟

雨露同行风雨同舟.jpg

活着,最有意义的事情,就是不遗余力地提升自己的认知,拓展自己的认知边界。

前言

源码地址

vue源码:github.com/vuejs/vue

迁出项目:git clone github.com/vuejs/vue.g…

2023.08.03更新

最新版的vue2源码需要使用pnpm来安装(官方推荐使用哪种包管理器来安装依赖,通过源码中的lock文件可以看出来),比如:

package-lock.json —— npm

yarn-lock.json —— yarn

pnpm-lock.yaml ——pnpm

安装过程中,puppeteer会比较耗时,耐心等待即可。

源码目录结构

dist —— 发布目录
example —— 范例,测试代码(探索源码使用)
flow —— flow类型声明
packages —— 核心代码外的独立库
scripts —— 构建脚本
src —— 源码
    compiler —— 编译器相关
    core —— 核心代码
        components —— 通用组件(如keep-alive)
        global-api —— 全局API
        instance —— 构造函数等
        observer —— 响应式相关
        util —— 工具函数
        vdom —— 虚拟dom相关
    platforms —— 平台相关
test —— 测试代码
types —— ts类型声明

调试环境搭建

1、安装依赖:npm i

2、安装rollup:npm i rollup -g (用于vue项目打包)

3、修改package.json中的dev脚本

"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:webfull-dev",

备注:
    添加 --sourcemap //用于查看运行时的源代码
    其中,-c scripts/config.js 指明配置文件
    TARGET:web-full-dev 指明输出文件配置项

4、运行开发命令:npm run dev

执行后,会生成编译后的vue.js

注意事项:项目存放路径上不要包含中文或特殊字符,否则打包时会报错

5、在html中引入Vue

<script src="../../dist/vue.js"></script>
术语:
rumtime:仅包含运行时,不包含编译器
common: cjs规范,用于webpack1
esm: ES模块,用于webpack2+
umd: universal module definition,兼容cjs和amd,用于浏览器

Vue源码初探

入口

//dev脚本
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
//通过dev脚本,可知使用的是scripts/config.js中的配置
//通过web-full-dev全局搜索,也可以定位到dev脚本使用的配置
// Runtime+compiler development build (Browser)
{ 
  'web-full-dev': { 
    entry: resolve('web/entry-runtime-with-compiler.js'), // ⼊⼝ 
    dest: resolve('dist/vue.js'),// ⽬标⽂件 
    format: 'umd', // 输出规范 
    env: 'development', 
    alias: { he: './entity-decoder' }, 
    banner, 
  }, 
}
//通过entry属性,可以找到打包的入口文件——entry-runtime-with-compiler.js
//这个文件导出了最终封装好的Vue

找到Vue的构造函数

1、在entry-runtime-with-compiler.js中可以看到:

import Vue from './runtime/index'

在src/platforms/web/runtime/index.js中引入Vue,在此基础上拓展了$mount方法,同时挂载了compile属性。

继续往上找~

2、在src/platforms/web/runtime/index.js中,大概做了如下事情:

首先,引入了Vue

import Vue from './instance/index'

然后,在Vue上挂载了特定平台下的工具方法,例如:

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

安装平台运行时指令和组件:

// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
//具体细节,源码一看便懂,比如:
//platformDirectives
export default {
  model,
  show
}

在Vue上首次挂载$mount方法:

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}
//mountComponent这个方法很重要

继续往上找~

3、在src/core/index.js中,做了如下事情:

首先,引入了Vue

import Vue from './instance/index'

然后,初始化全局API,并挂载了一些只读属性和可写的上下文

继续往上找~

4、在src/core/instance/index.js中,定义了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)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

构造函数中,执行了Vue的_init()方法,这个方法是在initMixin中挂载到Vue上的(这个过程发生在Vue源码构建时)

//initMixin方法
Vue.prototype._init = function (options?: Object) {
  //略
}

当我们在浏览器执行new Vue()时,一个Vue实例的旅程便从此开启了。

在浏览器中调试源码

1、编写测试文件:init.html

<!DOCTYPE html>
<html>

<head>
    <title>Vue源码剖析</title>
    <script src="../../dist/vue.js"></script>
</head>

<body>
    <div id="demo">
        <h1>初始化流程</h1>
        <p>{{lulu}}</p>
        
    </div>
    <script>
        // 创建实例
        const app = new Vue({
            el: '#demo',
            data:{lulu:'璐璐'}
        })
        console.log(app.$options.render);
    </script>
</body>
</html>

2、打开浏览器调试工具,在new Vue所在行打上断点,然后步入Vue的构造函数

至此,Vue的大门为你敞开,尽情地刷野打副本吧