Vue.js源码学习之开端

卫弘义
2023-12-01

源码构建

源码下载地址:https://github.com/vuejs/vue

源码目录

Vue源码位于src目录下,其目录结构如下:

src
├── compiler        # 编译相关 
├── core            # 核心代码 
├── platforms       # 不同平台的支持
├── server          # 服务端渲染
├── sfc             # .vue 文件解析
├── shared          # 共享代码
  • compiler

    compiler目录中包含Vue中所有与编译相关的代码,主要功能是把模板解析成ast语法树,ast语法树优化,代码生成等。编译工作可以在构建时借助webpack、vue-loader等插件进行离线编译,也可以在运行时直接使用包含构建功能的Vue。由于编译工作非常耗费性能,所以更推荐使用前者的离线编译模式。

  • core

    core目录中包含了Vue的核心代码,包括内置组件、全局API封装、Vue实例化、观察者、虚拟DOM、工具函数等,是学习Vue源码的重点。

  • platform

    platform中包含两个次级目录,代表两个入口。Vue是一个跨平台的MVVM框架,可以在web上运行,也可以配合weex在native客户端上运行。通过两个入口,分别打包运行在web上和weex上的Vue。

  • server

    server目录中包含与服务端渲染相关的逻辑,这部分代码在服务端的Node.js中运行,和在浏览器端运行的Vue不一样。

  • sfc

    在开发过程中,常常会通过webpack构建.vue单文件来编写组件。sfc目录中的代码会将.vue文件解析成一个JS对象。

  • shared

    shared目录中包含的是一些对工具方法定义,这些方法会被浏览器端的Vue和服务端的Vue所共享。

构建过程

package.json中,Vue源码的构建脚本为

{
    "script": {
        "build": "node scripts/build.js",
        "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
    	"build:weex": "npm run build -- weex",
    }
}

当运行脚本npm run build时,实际上时执行node scripts/build.js。Vue源码是基于Rollup构建的,与构建相关的配置均位于scripts目录下。后两条命令是在第一条命令的基础上,添加了环境参数。

以第一条命令为例,在scripts/build.js中,

let builds = require('./config').getAllBuilds()

// filter builds via command line arg
if (process.argv[2]) {
  const filters = process.argv[2].split(',')
  builds = builds.filter(b => {
    return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
  })
} else {
  // filter out weex builds by default
  builds = builds.filter(b => {
    return b.output.file.indexOf('weex') === -1
  })
}

build(builds)

上述代码中,builds是配置文件scripts/config.js中的所有配置选项,如果构建时有命令行参数process.argv[2],则会将输入的参数分割成一个数组filters,然后在builds中过滤出所有涉及到filters中各项的配置。若没有命令行参数,则默认采用weex。

scripts/config.js中的配置选项很多,提取出其中一部分进行分析

const builds = {
    // Runtime+compiler CommonJS build (CommonJS)
    'web-full-cjs-dev': {
        entry: resolve('web/entry-runtime-with-compiler.js'),
        dest: resolve('dist/vue.common.dev.js'),
        format: 'cjs',
        env: 'development',
        alias: { he: './entity-decoder' },
        banner
    },
    // Runtime only ES modules build (for bundlers)
    'web-runtime-esm': {
        entry: resolve('web/entry-runtime.js'),
        dest: resolve('dist/vue.runtime.esm.js'),
        format: 'es',
        banner
    },
    // runtime-only build (Browser)
    'web-runtime-dev': {
        entry: resolve('web/entry-runtime.js'),
        dest: resolve('dist/vue.runtime.js'),
        format: 'umd',
        env: 'development',
        banner
    },
}

在单个配置中,entry表示构建入口js文件地址;dest表示构建出口js文件地址;format表示构建出来的文件所遵循的规范,其中cjs表示CommonJS规范,es表示ES Module规范,umd表示UMD规范。

web-runtime-esm配置为例,其入口文件的地址为resolve('web/entry-runtime.js'),其中resolve函数定义如下

const aliases = require('./alias')
const resolve = p => {
  const base = p.split('/')[0]
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    return path.resolve(__dirname, '../', p)
  }
}

在上述代码中,首先获取在scripts/alias中定义的路径别名(先将相对路径转换成绝对路径,然后再取一个简短的路径别名),然后将传入的参数进行分割,取数组第一项,在这里被取出的baseweb,如果路径别名中存在web,则将web对应的路径与参数数组的剩余项进行拼接,找到最终路径。这里找到的是在web目录下的entry-runtime.js文件,也就是web-runtime-esm配置的入口文件。经过Rollup构建打包后,最终在dist目录下生成vue.runtime.esm.js文件。

Runtime Only VS Runtime + Compiler

使用vue-cli脚手架初始化Vue项目时,可以选择使用Runtime Only版本,还是Runtime + Compiler版本。这两个版本的区别在于编译工作时离线时完成 还是在线运行时进行。

  • Runtime Only

    若选择Runtime Only版本,则通常需要借助webpack、Vue-loader等工具将.vue文件编译成js文件。这一工作是在编译阶段完成的,所以在运行时只需要执行Vue.js代码,所以代码体积会更轻量。

  • Runtime + Compiler

    若选择Runtime + Compiler版本,则在运行时直接使用包含构建功能的Vue。在Vue2.0中,最终的渲染都是通过render函数进行的,如果定义了template属性,则需要先编译成render函数,再渲染。

    // 需要编译器编译成render函数
    new Vue({
        templete: '<div>{{ 编译 }}<div>'
    })
    
    // 不需要编译
    new Vue({
        render (h) {
            return h('div',this.hi)
        }
    })

    由于编译工作非常耗费性能,所以更推荐使用Runtime Only的离线编译模式。

 类似资料: