飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

vue 快速入门 系列 —— Vue(自身) 项目结构

时间:2022-01-17  作者:pengjiali  

其他章节请看:

vue 快速入门 系列

Vue(自身) 项目结构

前面我们已经陆续研究了 vue 的核心原理:数据侦测、模板和虚拟 DOM,都是偏底层的。本篇将和大家一起来看一下 vue 自身这个项目,了解它的目录结构,以及构建过程。

vue 的目录结构

将 vue 项目 下载到本地 git clone git@域名:vuejs/域名 域名

- 域名
    - dist                      // 构建后的文件
    - examples                  // 有几个用 vue 写的示例,直接是通过 <script> 方式。例如有经典的 todo,还有 markdown
    - flow                      // flow 相关。flow 是 JAVASCRIPT 的静态类型检查器
    - packages                  // 这 4 个包在 npm 中都能搜索到
        - vue-server-renderer
        - vue-template-compiler
        - weex-template-compiler
        - weex-vue-framework
    - scripts                   // 构建相关的脚本和配置文件。还有 gitHooks
    - src
        - compiler              // 编译器。与模板编译相关的代码,例如解析器、优化器、生成器等。从 core 中分离出来或许是因为有的版本不需要它。
        - core                  // vue 的核心代码
            - components        // 有 keep-alive 组件
            - global-api        // 全局 api 的代码。例如 域名
            - instance          // vue 的构造函数和实例方法。例如 域名otype.$set
            - observer          // 侦测数据变化相关代码
            - util              // 工具相关。例如 域名、域名、next-域名
            - vdom              // 虚拟 dom
        - platforms             // 平台相关
            - web               
            - weex              // 阿里巴巴发起的跨平台用户界面开发框架
        - sfc                   // 将单文件组件 (*.vue) 文件解析为 SFC 描述符对象
            - 域名
        - shared                // 公用的工具代码。在 vscode 中搜索 `shared/`,可发现有 76 个文件引用了它
            - 域名           // 工具模块
    - test                      // 测试相关
    - types                     // TypeScript 相关

Tip: flow 和 githooks 就不节外生枝了

构建版本

dist 目录下有很多版本的 vue,我们需要了解一下它们的差异。

完整版:有 域名域名域名等。

运行时版本:包含 runtime 的,例如 域名域名.js域名域名

完整版包括运行时和编译器,而运行时基本上就是完整版除去编译器的其它一切。

Tip编译器,用来将模板字符串编译成为 JavaScript 渲染函数的代码。在 模板 一文中已介绍。

// 需要编译器
new Vue({
  template: \'<div>{{ hi }}</div>\'
})

// 不需要编译器
new Vue({
  render (h) {
    return h(\'div\', 域名)
  }
})

UMD版本:umd 版本的文件通过 <script> 标签直接在浏览器中使用。有域名域名域名域名.js

CommonJS 版本:包含 common 的,例如 域名域名域名。主要给的打包工具使用,入 webpack 1。

ES Module 版本:包含 esm 的,例如 域名域名.js。主要配合(或现代)的打包工具,比如 webpack 2 或 Rollup。

Tip:有关构建版本更详细的介绍请看 官网

使用 vue 的哪个版本(import \'vue\')

现代打包工具,通过 importrequire 引入 vue,使用的都是 域名.js

为什么是这样?请看实验。

准备一个项目,有 webpack,通过 npm 安装 vue,最后能打包就好了。

Tip:webpack 的简单使用可以看 初步认识 webpack

域名 中就写一行代码:

import \'vue\'

然后构建生成 域名:

test-project> npx webpack --mode development
Hash: e9412c758fa785a2fd70
Version: webpack 域名.0
Time: 289ms
Built at: 2022/01/16 上午9:55:41
  Asset     Size  Chunks             Chunk Names
域名  250 KiB    main  [emitted]  main
Entrypoint main = 域名
[./node_modules/webpack/buildin/域名] (webpack)/buildin/域名 472 bytes {main} [built]
[./src/域名] 12 bytes {main} [built]
    + 4 hidden modules
// 域名
...
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/域名.js\");\n\n\n//# sourceURL=webpack:///./src/域名?");

我们在域名 中发现 域名.js

如果改为 require(\'vue\'),仍然是 域名.js

eval("__webpack_require__(/*! vue */ \"./node_modules/vue/dist/域名.js\")\n\n//# sourceURL=webpack:///./src/域名?");

如果我们删除 vue域名 的一行代码,再次打包:

// node_modules/vue/域名
{
  "name": "vue",
  "version": "域名",
  "description": "Reactive, component-oriented view layer for modern web interfaces.",
  "main": "dist/域名域名",
- "module": "dist/域名.js",

会发现 域名 中引入的变成 域名域名

eval("__webpack_require__(/*! vue */ \"./node_modules/vue/dist/域名域名\")\n\n//# sourceURL=webpack:///./src/域名?");

:在 vue-cli 的项目中,即使只删除 "module": "dist/域名.js",,使用 vue 版本也不会变成 域名域名

构建分析

dist 目录下有很多版本的 vue。每次运行 npm run build 就会重新生成一遍:

域名> npm run build

> vue@域名 build
> node scripts/域名

dist\域名域名 域名
dist\域名域名 域名 (gzipped: 域名)
dist\域名.js 域名
dist\域名.js 域名 (gzipped: 域名)
dist\域名.js 域名
dist\域名 域名
dist\域名域名 域名
dist\域名域名 域名 (gzipped: 域名)
dist\域名 域名
dist\域名.js 域名 (gzipped: 域名)
dist\域名 域名
dist\域名 域名 (gzipped: 域名)
packages\vue-template-compiler\域名 域名
packages\vue-template-compiler\域名 域名
packages\vue-server-renderer\域名 域名
packages\vue-server-renderer\域名 域名 (gzipped: 域名)
packages\vue-server-renderer\域名 域名
packages\vue-server-renderer\server-域名 域名
packages\vue-server-renderer\client-域名 域名

Tipnpm run build 来自 域名,运行前需要安装依赖 npm i

只生成 域名.js

如何让 npm run build 只生成 域名.js 这一个文件?我们先分析:

首先,运行 npm run build 就是运行 node scripts/域名,也就是执行 域名/scripts/域名 这个文件。

如果我们将这个文件内容替换成 域名(\'i am 域名\'),再次编译,发现什么事都不会去做,仅仅输出 i am 域名

域名> npm run build    

> vue@域名 build
> node scripts/域名

i am 域名

于是我们知道应该从 域名 入手。核心代码如下:

// 域名

// 现代构建工具
const rollup = require(\'rollup\')

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

// 构建
build(builds)

里面提到 域名getAllBuilds 方法:

域名llBuilds = () => 域名(builds).map(genConfig)

最后定位到 builds 变量:

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

修改 builds 并重新打包:

// 只保留一个
const builds = {
  // Runtime only ES modules build (for bundlers)
  \'web-runtime-esm\': {
    entry: resolve(\'web/entry-域名\'),
    dest: resolve(\'dist/域名.js\'),
    format: \'es\',
    banner
  }
}
域名> npm run build

> vue@域名 build
> node scripts/域名

dist\域名.js 域名

至此,每次编译,则只会生成一个文件。

构建 域名.js 的过程

web/entry-域名

从下面这段代码,我们猜测 域名.js 的入口是 web/entry-域名:

\'web-runtime-esm\': {
    entry: resolve(\'web/entry-域名\'),
    dest: resolve(\'dist/域名.js\'),
    format: \'es\',
    banner
  }
// 域名/src/platforms/web/entry-域名 全部内容:

/* @flow */

import Vue from \'./runtime/index\'

export default Vue

替换 entry-域名 的内容如下,重新构建:

// entry-域名
const Vue = function () { }
export default Vue
域名> npm run build

> vue@域名 build
> node scripts/域名

dist\域名.js 域名
// dist/域名.js 全部内容:

/*!
 * 域名 域名
 * (c) 2014-2022 Evan You
 * Released under the MIT License.
 */
var Vue = function () { };

export default Vue;

根据打包后的内容,说明 web/entry-域名 确实就是入口。

runtime/index

根据上文的分析,我们已知晓 域名.js 的构建的过程就是在 runtime/index 中定义的。以下是与 Vue 相关的代码:

// 域名/src/platforms/web/runtime/域名

/* @flow */

import Vue from \'core/index\'

// install platform specific utils
// 安装平台特定的工具
域名UseProp = mustUseProp
域名servedTag = isReservedTag
域名servedAttr = isReservedAttr
域名agNamespace = getTagNamespace
域名knownElement = isUnknownElement

// install platform runtime directives & components
extend(域名ctives, platformDirectives)
extend(域名onents, platformComponents)

// install platform patch function
域名tch__ = inBrowser ? patch : noop

// public mount method
域名otype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

export default Vue

关键代码就是第一行 import Vue from \'core/index\',也即是引入 vue 的核心代码。

core/index
// 域名/src/core/域名 全部代码:

// 返回 Vue 构造函数,并准备好实例方法
import Vue from \'./instance/index\'
import { initGlobalAPI } from \'./global-api/index\'
import { isServerRendering } from \'core/util/env\'
import { FunctionalRenderContext } from \'core/vdom/create-functional-component\'

// 初始化全局 api
initGlobalAPI(Vue)

域名neProperty(域名otype, \'$isServer\', {
  get: isServerRendering
})

域名neProperty(域名otype, \'$ssrContext\', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$域名ontext
  }
})

// expose FunctionalRenderContext for ssr runtime helper installation
域名neProperty(Vue, \'FunctionalRenderContext\', {
  value: FunctionalRenderContext
})

域名ion = \'__VERSION__\'

export default Vue

关键代码是 instance/index(构造函数和实例方法) 和 global-api/index(全局方法):

// 域名/src/core/instance/域名 全部代码:

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\'
// 构造函数
function Vue (options) {
  if (域名_ENV !== \'production\' &&
    !(this instanceof Vue)
  ) {
    warn(\'Vue is a constructor and should be called with the `new` keyword\')
  }
  域名t(options)
}

initMixin(Vue)
// 状态相关
stateMixin(Vue)
// 事件相关
eventsMixin(Vue)
// 生命周期相关
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue
// 域名/src/core/global-api/域名 

/* @flow */

import config from \'../config\'
import { initUse } from \'./use\'
import { initMixin } from \'./mixin\'
import { initExtend } from \'./extend\'
import { initAssetRegisters } from \'./assets\'
import { set, del } from \'../observer/index\'
import { ASSET_TYPES } from \'shared/constants\'
import builtInComponents from \'../components/index\'
import { observe } from \'core/observer/index\'

import {
  warn,
  extend,
  nextTick,
  mergeOptions,
  defineReactive
} from \'../util/index\'

// 初始化全局 api
export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  域名 = () => config
  if (域名_ENV !== \'production\') {
    域名 = () => {
      warn(
        \'Do not replace the 域名ig object, set individual fields instead.\'
      )
    }
  }
  域名neProperty(Vue, \'config\', configDef)

  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  域名 = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  // 定义全局 api:set、delete、nextTick...
  域名 = set
  域名te = del
  域名Tick = nextTick

  // 2.6 explicit observable API
  域名rvable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }

  域名ons = 域名te(null)
  域名ach(type => {
    域名ons[type + \'s\'] = 域名te(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex\'s multi-instance scenarios.
  域名e = Vue

  extend(域名onents, builtInComponents)

  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}

其他章节请看:

vue 快速入门 系列

标签:vue编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。