「深入」Vue源码学习(一)- 运行与调试

1,199 阅读4分钟

(封面图片来源于网络,侵删)

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

vue作为目前最流行的前端框架之一,其设计理念、开发效率给前端开发者们提供了许多便利。随着vue3的出现,vue在前端领域中更是占据了举足轻重的地位。鉴于对vue的热爱,我也一直想从源码开始深入学习一下,但前前后后看了几次源码都一直没提笔,这次终于开始了。 本文针对vue2.6.14的源码进行学习,最近会持续更新。

一、vue源码下载

  • github上拉取分支。
  • github上download压缩文件。

二、vue构建

使用开发工具打开vue源码,安装项目相关依赖,安装完成会在当前文件下生成node-modules文件夹。

cd vue-dev
npm install

执行npm run build指令,生成打包后的文件,注:这里生成的文件在后续本地调试验证时可直接利用script标签引入。这步执行后会在当前目录下生成dist文件夹,dist文件夹下存放了vue生成的打包后文件。在README.md文件中,可查询这几种不同文件之间的区别。具体如何打包,在后面文章中介绍。 登录逻辑图

vue在执行完上述操作之后,就完成了引入文件的生成工作。

三、vue源码执行

我们知道,package.js文件中,定义了脚本的执行方式。找到packages.js文件下的script属性,查找dev命令。可看到vue-dev项目,使用了rollup进行打包,同时在执行dev命令的时候,主要读取了scripts/config.js下的文件,TARGET为web-full-dev。

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

根据命令找到scripts/configs.js文件。在config.js文件下,我们可以看到有两个函数,其他均为变量,从函数入手,找到该文件的执行入口。 登录逻辑图 在dev命令下,有TARGET属性,TARGET:web-full-dev,该TARGET属性作为process.env的系统变量被读取,同时作为参数传给genConfig函数,genConfig函数会根据当前TARGET属性,读取响应配置。在这里文件执行genConfig函数,同时作为模块函数被导出。

// script/config.js
// 根据TARGET属性,判断执行
if (process.env.TARGET) {
  module.exports = genConfig(process.env.TARGET)
} else {
  exports.getBuild = genConfig
  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}

在genConfig中,主要生成了rollup的配置属性,其中包括input、output、plugins、external等,这些属性的内容实际又来源于builds对象,该对象中定义了TARGET属性对应的配置内容,builds对象中,定义了基本属性,可根据web-full-dev查找。根据设置的配置项,可找到实际的入口文件,在web/entry-runtime-with-compiler.js中。同时根据script/alias.js文件夹下对web别名的定义,可以定位到其所在目录为src/platforms/web

// web-full-dev属性对应配置项
const builds = {
    // 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
    },
}
// genConfig函数读取了配置属性
function genConfig (name) {
  const opts = builds[name]
  const config = {
    input: opts.entry,
    external: opts.external,
    plugins: [...],
    output: {
      file: opts.dest,
      ...
    },
    onwarn: (msg, warn) => {
      if (!/Circular/.test(msg)) {
        warn(msg)
      }
    }
  }
// scrpit/alias.js 别名文件
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'),
  weex: resolve('src/platforms/weex'),
  server: resolve('src/server'),
  sfc: resolve('src/sfc')
}

四、src文件夹下的执行方式

通过config文件内容执行,我们找到了源码程序的入口文件,在web/entry-runtime-with-compiler.js,同时在alias.js文件下,可以找到web别名对应的文件夹路径。在该文件下,找到Vue的定义文件,在runtime/index下,runtime/index文件中,继续查找Vue定义文件,在core/index下,继续查看Core/index文件,查找到instance/index文件。

因此,一步步查找,可以找到Vue的真正入口,在core/instance/index.js文件下。

// alias.js 
web: resolve('src/platforms/web'),  寻找web别名对应的文件夹在src/platforms/web下

// web/entry-runtime-with-compiler.js
import Vue from './runtime/index'  该文件夹中引入的Vue入口文件

// runtime/index.js
import Vue from 'core/index' 寻找到core下的index文件

// core/index.js
import Vue from './instance/index' 寻找到真正入口,在instance中
登录逻辑图

在core/instance/index.js文件中,定义了Vue

// 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)
}

五、如何调试vue源文件

在执行完build命令后,会在dist文件夹下生成不同文件,上方截图已经表明,vue.js一般包含了整个运行与编译环节的所有代码,我们一般通过引入该文件,进行代码调试。

在vue-dev项目中,也提供了测试文件夹。在src/example下,我们可以在该文件夹下,自定义文件夹或文件,引入dise/vue.js就可以对我们的源码进行相关调试验证。 登录逻辑图

本文主要从vue源码的构建、vue源码如何查找入口、vue源码如何调试三方面进行了详细介绍,下一章将学习vue选项初始化与数据响应式原理。