学习Vue源码(准备工作&寻找入口文件)

233 阅读3分钟

准备

源码的获取

地址: github.com/vuejs/vue

源码目录结构

├── benchmarks                  性能、基准测试
├── dist                        构建打包的输出目录
├── examples                    案例目录
├── flow                        flow 语法的类型声明
├── packages                    一些额外的包,比如:负责服务端渲染的包 vue-server-renderer、配合 vue-loader 使用的的 vue-template-compiler,还有 weex 相关的
│   ├── vue-server-renderer
│   ├── vue-template-compiler
│   ├── weex-template-compiler
│   └── weex-vue-framework
├── scripts                     所有的配置文件的存放位置,比如 rollup 的配置文件
├── src                         vue 源码目录
│   ├── compiler                编译器
│   ├── core                    Vue 核心库
│   │   ├── components          全局组件,比如 keep-alive
│   │   ├── config.js           一些默认配置项
│   │   ├── global-api          全局 API,比如熟悉的:Vue.use()、Vue.component() 等
│   │   ├── instance            Vue 实例相关的,比如 Vue 构造函数就在这个目录下
│   │   ├── observer            响应式原理
│   │   ├── util                工具方法
│   │   └── vdom                虚拟 DOM 相关,比如熟悉的 patch 算法就在这儿
│   ├── platforms               平台相关的编译器代码
│   │   ├── web
│   │   └── weex
│   ├── server                  服务端渲染相关
├── test                        测试目录
├── types                       TS 类型声明

安装依赖

npm i

sourcemap

学习源码的过程中需要我们可以在控制台看见执行代码的定位所在,所以需要打开代码地图

在 package.json 文件中的 dev 脚本添加参数 --sourcemap

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

运行项目

npm run dev

关于运行报错(plugin Rollup Core) Error: Could not load 的解决方案

原因:rollup-plugin-alias对windows的兼容不好

1.下载 github.com/ideayuye/ro… 并覆盖掉本地文件夹 \node_modules\rollup-plugin-alias

2.进入rollup-plugin-alias文件夹,依次执行npm i ,npm run build.

3.重新启动vue项目。

寻找入口文件

npm run dev

像rollup,webpack这类打包工具构建的时候会设置一个配置文件,里面会设置入口,打包工具会根据这个配置文件找到所有的依赖,再打包到一个文件。

npm run dev, 我们找到 package.json 中script下的 dev,可得知rollup打包构建的时候执行的这条命令。

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

scripts/config.js ----- 配置文件

--sourcemap ----- 代码地图

--environment TARGET --- 设置环境变量 TARGET 为 web-full-dev

script/ config.js

// srcipt/config.js
...

// 判断环境变量是否有 TARGET
// 如果有的话 使用 genConfig() 生成 rollup 配置文件
if (process.env.TARGET) {
  module.exports = genConfig(process.env.TARGET)
} else {
  exports.getBuild = genConfig
  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}

由于有设置环境变量TARGET,genConfig函数执行并且传入这个环境变量,值为 web-full-dev,那么这个环境变量有什么用呢?

genConfig

// srcipt/config.js

function genConfig (name) {
  const opts = builds[name]
  const config = {
    input: opts.entry,
    external: opts.external,
    plugins: [
      flow(),
      alias(Object.assign({}, aliases, opts.alias))
    ].concat(opts.plugins || []),
    output: {
      file: opts.dest,
      format: opts.format,
      banner: opts.banner,
      name: opts.moduleName || 'Vue'
    },
    onwarn: (msg, warn) => {
      if (!/Circular/.test(msg)) {
        warn(msg)
      }
    }
  }

接着继续找到变量 build, 根据name 找到匹配的变量 "web-full-dev"

// srcipt/config.js
...
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
  },
  
  ...
}

entry 打包带文件入口 再接着查看resolve

//  script/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'),
  weex: resolve('src/platforms/weex'),
  server: resolve('src/server'),
  sfc: resolve('src/sfc')
}
// srcipt/config.js

...

const aliases = require('./alias')
const resolve = p => {
  // 根据路径中的前半部分去alias中找别名
  const base = p.split('/')[0]
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    return path.resolve(__dirname, '../', p)
  }
}

...

结果

截取参数p 即传入的 "web/entry-runtime-with-compiler.js" 中的斜杆, 即 变量 base === web

根据base 可得到 aliases[base] === resolve('src/platforms/web')

p.slice(base.length + 1) 根据斜杆切割参数p 得到的是 entry-runtime-with-compiler.js

最终 我们可以得出

genConfig函数中配置对象 config的input值等于 绝对路径

src/platforms/web/entry-runtime-with-compiler.js

它就是我们要找到入口文件,这个入口包括了编译器和运行时