vue 快速入门 系列 —— Vue(自身) 项目结构
其他章节请看:
Vue(自身) 项目结构
前面我们已经陆续研究了 vue 的核心原理:数据侦测、模板和虚拟 DOM,都是偏底层的。本篇将和大家一起来看一下 vue 自身这个项目,了解它的目录结构,以及构建过程。
vue 的目录结构
将 vue 项目 下载到本地 git clone git@github.com:vuejs/vue.git vuev2.5.20
- vuev2.5.20
- 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 的代码。例如 Vue.set
- instance // vue 的构造函数和实例方法。例如 Vue.prototype.$set
- observer // 侦测数据变化相关代码
- util // 工具相关。例如 env.js、error.js、next-tick.js
- vdom // 虚拟 dom
- platforms // 平台相关
- web
- weex // 阿里巴巴发起的跨平台用户界面开发框架
- sfc // 将单文件组件 (*.vue) 文件解析为 SFC 描述符对象
- parser.js
- shared // 公用的工具代码。在 vscode 中搜索 `shared/`,可发现有 76 个文件引用了它
- util.js // 工具模块
- test // 测试相关
- types // TypeScript 相关
构建版本
dist 目录下有很多版本的 vue,我们需要了解一下它们的差异。
完整版:有 vue.js、vue.esm.js、vue.common.js等。
运行时版本:包含 runtime 的,例如 vue.runtime.js、vue.runtime.esm.js、vue.runtime.common.js。
完整版包括运行时和编译器,而运行时基本上就是完整版除去编译器的其它一切。
Tip:编译器,用来将模板字符串编译成为 JavaScript 渲染函数的代码。在 模板 一文中已介绍。
// 需要编译器
new Vue({
template: '<div>{{ hi }}</div>'
})
// 不需要编译器
new Vue({
render (h) {
return h('div', this.hi)
}
})
UMD版本:umd 版本的文件通过 <script> 标签直接在浏览器中使用。有vue.js、vue.runtime.js、vue.min.js、vue.runtime.min.js
CommonJS 版本:包含 common 的,例如 vue.common.js、vue.runtime.common.js。主要给旧的打包工具使用,入 webpack 1。
ES Module 版本:包含 esm 的,例如 vue.esm.js、vue.runtime.esm.js。主要配合新(或现代)的打包工具,比如 webpack 2 或 Rollup。
Tip:有关构建版本更详细的介绍请看 官网
使用 vue 的哪个版本(import 'vue')
现代打包工具,通过 import 或 require 引入 vue,使用的都是 vue.runtime.esm.js。
为什么是这样?请看实验。
准备一个项目,有 webpack,通过 npm 安装 vue,最后能打包就好了。
Tip:webpack 的简单使用可以看 初步认识 webpack
在 index.js 中就写一行代码:
import 'vue'
然后构建生成 main.js:
test-project> npx webpack --mode development
Hash: e9412c758fa785a2fd70
Version: webpack 4.46.0
Time: 289ms
Built at: 2022/01/16 上午9:55:41
Asset Size Chunks Chunk Names
main.js 250 KiB main [emitted] main
Entrypoint main = main.js
[./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {main} [built]
[./src/index.js] 12 bytes {main} [built]
+ 4 hidden modules
// main.js
...
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.runtime.esm.js\");\n\n\n//# sourceURL=webpack:///./src/index.js?");
我们在main.js 中发现 vue.runtime.esm.js。
如果改为 require('vue'),仍然是 vue.runtime.esm.js。
eval("__webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.runtime.esm.js\")\n\n//# sourceURL=webpack:///./src/index.js?");
如果我们删除 vue 中 package.json 的一行代码,再次打包:
// node_modules/vue/package.json
{
"name": "vue",
"version": "2.6.14",
"description": "Reactive, component-oriented view layer for modern web interfaces.",
"main": "dist/vue.runtime.common.js",
- "module": "dist/vue.runtime.esm.js",
会发现 main.js 中引入的变成 vue.runtime.common.js
eval("__webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.runtime.common.js\")\n\n//# sourceURL=webpack:///./src/index.js?");
注:在 vue-cli 的项目中,即使只删除 "module": "dist/vue.runtime.esm.js",,使用 vue 版本也不会变成 vue.runtime.common.js。
构建分析
dist 目录下有很多版本的 vue。每次运行 npm run build 就会重新生成一遍:
vuev2.5.20> npm run build
> vue@2.6.14 build
> node scripts/build.js
dist\vue.runtime.common.dev.js 227.52kb
dist\vue.runtime.common.prod.js 63.60kb (gzipped: 22.98kb)
dist\vue.common.dev.js 326.08kb
dist\vue.common.prod.js 91.81kb (gzipped: 33.41kb)
dist\vue.runtime.esm.js 231.45kb
dist\vue.esm.js 331.88kb
dist\vue.esm.browser.js 321.26kb
dist\vue.esm.browser.min.js 91.26kb (gzipped: 33.38kb)
dist\vue.runtime.js 242.70kb
dist\vue.runtime.min.js 63.76kb (gzipped: 23.04kb)
dist\vue.js 347.56kb
dist\vue.min.js 91.98kb (gzipped: 33.47kb)
packages\vue-template-compiler\build.js 145.59kb
packages\vue-template-compiler\browser.js 253.25kb
packages\vue-server-renderer\build.dev.js 254.91kb
packages\vue-server-renderer\build.prod.js 79.47kb (gzipped: 28.99kb)
packages\vue-server-renderer\basic.js 340.69kb
packages\vue-server-renderer\server-plugin.js 4.00kb
packages\vue-server-renderer\client-plugin.js 4.02kb
Tip:npm run build 来自 package.json,运行前需要安装依赖 npm i。
只生成 vue.runtime.esm.js
如何让 npm run build 只生成 vue.runtime.esm.js 这一个文件?我们先分析:
首先,运行 npm run build 就是运行 node scripts/build.js,也就是执行 vuev2.5.20/scripts/build.js 这个文件。
如果我们将这个文件内容替换成 console.log('i am build.js'),再次编译,发现什么事都不会去做,仅仅输出 i am build.js:
vuev2.5.20> npm run build
> vue@2.6.14 build
> node scripts/build.js
i am build.js
于是我们知道应该从 build.js 入手。核心代码如下:
// build.js
// 现代构建工具
const rollup = require('rollup')
let builds = require('./config').getAllBuilds()
// 构建
build(builds)
里面提到 config.js 的 getAllBuilds 方法:
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
最后定位到 builds 变量:
const builds = {
...
// 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+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
},
...
}
修改 builds 并重新打包:
// 只保留一个
const builds = {
// 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
}
}
vuev2.5.20> npm run build
> vue@2.6.14 build
> node scripts/build.js
dist\vue.runtime.esm.js 231.45kb
至此,每次编译,则只会生成一个文件。
构建 vue.runtime.esm.js 的过程
web/entry-runtime.js
从下面这段代码,我们猜测 vue.runtime.esm.js 的入口是 web/entry-runtime.js:
'web-runtime-esm': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.esm.js'),
format: 'es',
banner
}
// vuev2.5.20/src/platforms/web/entry-runtime.js 全部内容:
/* @flow */
import Vue from './runtime/index'
export default Vue
替换 entry-runtime.js 的内容如下,重新构建:
// entry-runtime.js
const Vue = function () { }
export default Vue
vuev2.5.20> npm run build
> vue@2.6.14 build
> node scripts/build.js
dist\vue.runtime.esm.js 0.13kb
// dist/vue.runtime.esm.js 全部内容:
/*!
* Vue.js v2.6.14
* (c) 2014-2022 Evan You
* Released under the MIT License.
*/
var Vue = function () { };
export default Vue;
根据打包后的内容,说明 web/entry-runtime.js 确实就是入口。
runtime/index
根据上文的分析,我们已知晓 vue.runtime.esm.js 的构建的过程就是在 runtime/index 中定义的。以下是与 Vue 相关的代码:
// vuev2.5.20/src/platforms/web/runtime/index.js
/* @flow */
import Vue from 'core/index'
// install platform specific utils
// 安装平台特定的工具
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)
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method
Vue.prototype.$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
// vuev2.5.20/src/core/index.js 全部代码:
// 返回 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)
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
Vue.version = '__VERSION__'
export default Vue
关键代码是 instance/index(构造函数和实例方法) 和 global-api/index(全局方法):
// vuev2.5.20/src/core/instance/index.js 全部代码:
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 (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)
export default Vue
// vuev2.5.20/src/core/global-api/index.js
/* @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 = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
Object.defineProperty(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.
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
// 定义全局 api:set、delete、nextTick...
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
extend(Vue.options.components, builtInComponents)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
其他章节请看:
vue 快速入门 系列 —— Vue(自身) 项目结构的更多相关文章
- vue 快速入门 系列 —— vue loader 扩展
其他章节请看: vue 快速入门 系列 vue loader 扩展 在vue loader一文中,我们学会了从零搭建一个简单的,用于单文件组件开发的脚手架.本篇将在此基础上继续引入一些常用的库:vue ...
- vue 快速入门 系列 —— vue 的基础应用(上)
其他章节请看: vue 快速入门 系列 vue 的基础应用(上) Tip: vue 的基础应用分上下两篇,上篇是基础,下篇是应用. 在初步认识 vue一文中,我们已经写了一个 vue 的 hello- ...
- vue 快速入门 系列 —— vue loader 上
其他章节请看: vue 快速入门 系列 vue loader 上 通过前面"webpack 系列"的学习,我们知道如何用 webpack 实现一个不成熟的脚手架,比如提供开发环境和 ...
- vue 快速入门 系列 —— vue loader 下
其他章节请看: vue 快速入门 系列 vue loader 下 CSS Modules CSS Modules 是一个流行的,用于模块化和组合 CSS 的系统.vue-loader 提供了与 CSS ...
- vue 快速入门 系列 —— vue 的基础应用(下)
其他章节请看: vue 快速入门 系列 vue 的基础应用(下) 上篇聚焦于基础知识的介绍:本篇聚焦于基础知识的应用. 递归组件 组件是可以在它们自己的模板中调用自身的.不过它们只能通过 name 选 ...
- vue 快速入门 系列 —— Vue 实例的初始化过程
其他章节请看: vue 快速入门 系列 Vue 实例的初始化过程 书接上文,每次调用 new Vue() 都会执行 Vue.prototype._init() 方法.倘若你看过 jQuery 的源码, ...
- vue 快速入门 系列 —— vue-cli 下
其他章节请看: vue 快速入门 系列 Vue CLI 4.x 下 在 vue loader 一文中我们已经学会从零搭建一个简单的,用于单文件组件开发的脚手架:本篇,我们将全面学习 vue-cli 这 ...
- vue 快速入门 系列 —— vue-router
其他章节请看: vue 快速入门 系列 Vue Router Vue Router 是 Vue.js 官方的路由管理器.它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌. 什么是路由 ...
- vue 快速入门 系列 —— vue-cli 上
其他章节请看: vue 快速入门 系列 Vue CLI 4.x 上 在 vue loader 一文中我们已经学会从零搭建一个简单的,用于单文件组件开发的脚手架:本篇,我们将全面学习 vue-cli 这 ...
随机推荐
- Table.RowCount行列计数…Count(Power Query 之 M 语言)
数据源: 任意五行两列 目标: 计算行数(包括空行) 操作过程: [转换]>[对行进行计数] M公式: = Table.RowCount( 表 ) 扩展: 对表中列进行计数:= Table.C ...
- 添加备注信息(Project)
<Project2016 企业项目管理实践>张会斌 董方好 编著 就在任务信息的[高级]选项卡隔壁,还有一个[备注]选项卡,可别拿备注不当回事,因为任务名称的字数不能太多. 好吧,张同学也 ...
- SpringBoot(SpringMVC)使用addViewControllers设置统一请求URL重定向配置
只需要在配置中重写 addViewControllers方法 import org.springframework.context.annotation.Configuration; import o ...
- Centos7使用Docker启动elasticsearch服务秒退
首先查看docker启动日志 docker logs -f 容器id 查看报错信息 OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepG ...
- JAVA获取请求的IP地址
private static final String[] ADDR_HEADER = { "X-Forwarded-For", "Proxy-Client-IP&quo ...
- 重学c#系列——datetime 和 datetimeoffset[二十一]
前言 简单介绍一下datetime和 datetimeoffset. 正文 了解一个国家的文化,就要了解一个国家的历史. 要了解datetimeoffset,那么很有必要了解一下datetime. 表 ...
- Visual Studio Code常用快捷键
说明 以下快捷键适用于windows环境下, Mac请将ctrl替换为command按键: 部分快捷键或不一样. 查看VSCode快捷键定义: settings -> keymaps. 目前使用 ...
- 【LeetCode】266. Palindrome Permutation 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 日期 题目地址:https://leetcode ...
- 【剑指Offer】09. 用两个栈实现队列 解题报告(python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人微信公众号:负雪明烛 目录 题目描述 解题方法 一个栈用来保存输入,一个栈用来输出 日 ...
- LeetCode1240铺瓷砖
题目 n*m的矩阵,只用正方形铺.求最少正方形个数. n,m<=13 思路 贪心: 加入是最大的正方形,显然行不通,比如n=11,m=13.那么贪心策略是1个11,其余是大小为2的正方形5个,大 ...