Vue 源码设计与构建

简介: Vue 源码设计与构建

Vue源码设计与构建

源码设计

Vue.js 的源码都在 src 目录下,其目录结构如下。

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

从 Vue.js 的目录设计可以看到,作者把功能模块拆分的非常清楚,相关的逻辑放在一个独立的目录下维护,并且把复用的代码也抽成一个独立目录。

这样的目录设计让代码的阅读性和可维护性都变强,是非常值得学习和推敲的。

weex

Weex (opens new window)致力于使开发者能基于通用跨平台的 Web 开发语言和开发经验,来构建 Android、iOS 和 Web 应用。简单来说,在集成了 WeexSDK 之后,你可以使用 JavaScript 语言和前端开发经验来开发移动应用。

Weex 渲染引擎与 DSL 语法层是分开的,Weex 并不强依赖任何特定的前端框架。目前 Vue.js 和 Rax 这两个前端框架被广泛应用于 Weex 页面开发,同时 Weex 也对这两个前端框架提供了最完善的支持。Weex 的另一个主要目标是跟进流行的 Web 开发技术并将其和原生开发的技术结合,实现开发效率和运行性能的高度统一。在开发阶段,一个 Weex 页面就像开发普通网页一样;在运行时,Weex 页面又充分利用了各种操作系统的原生组件和能力。

源码构建

Vue.js 源码是基于 Rollup (opens new window)构建的,它的构建相关配置都在 scripts 目录下

通常一个基于 NPM 托管的项目都会有一个 package.json 文件,它是对项目的描述文件,它的内容实际上是一个标准的 JSON 对象。

我们通常会配置 script 字段作为 NPM 的执行脚本,Vue.js 源码构建的脚本如下:

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

这里总共有 3 条命令,作用都是构建 Vue.js,后面 2 条是在第一条命令的基础上,添加一些环境参数。

当在命令行运行 npm run build 的时候,实际上就会执行 node scripts/build.js,接下来我们来看看它实际是怎么构建的。

我们对于构建过程分析是基于源码的,先打开构建的入口 JS 文件,在 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)
复制代码

这段代码逻辑非常简单,先从配置文件读取配置,再通过命令行参数对构建配置做过滤,这样就可以构建出不同用途的 Vue.js 了。接下来我们看一下配置文件,在 scripts/config.js 中:

const builds = {
  // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
  'web-runtime-cjs-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.dev.js'),
    format: 'cjs',
    env: 'development',
    banner
  },
  'web-runtime-cjs-prod': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.prod.js'),
    format: 'cjs',
    env: 'production',
    banner
  },
  // 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
  },
  ...
}
复制代码

这里列举了一些 Vue.js 构建的配置,关于还有一些服务端渲染 webpack 插件以及 weex 的打包配置就不列举了。

对于单个配置,它是遵循 Rollup 的构建规则的。其中 entry 属性表示构建的入口 JS 文件地址,dest 属性表示构建后的 JS 文件地址。format 属性表示构建的格式,cjs 表示构建出来的文件遵循 CommonJS 规范,es 表示构建出来的文件遵循 ES Module 规范。 umd 表示构建出来的文件遵循 UMD 规范。

以 web-runtime-cjs 配置为例,它的 entry 是 resolve('web/entry-runtime.js'),先来看一下 resolve 函数的定义。

源码目录:scripts/config.js

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)
  }
}
复制代码

这里的 resolve 函数实现非常简单,它先把 resolve 函数传入的参数 p 通过 / 做了分割成数组,然后取数组第一个元素设置为 base。在我们这个例子中,参数 p 是 web/entry-runtime.js,那么 base 则为 web。base 并不是实际的路径,它的真实路径借助了别名的配置,我们来看一下别名配置的代码,在 scripts/alias 中:

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')
}
复制代码

很显然,这里 web 对应的真实的路径是 path.resolve(__dirname, '../src/platforms/web'),这个路径就找到了 Vue.js 源码的 web 目录。然后 resolve 函数通过 path.resolve(aliases[base], p.slice(base.length + 1)) 找到了最终路径,它就是 Vue.js 源码 web 目录下的 entry-runtime.js。因此,web-runtime-cjs 配置对应的入口文件就找到了。

它经过 Rollup 的构建打包后,最终会在 dist 目录下生成 vue.runtime.common.js


相关文章
|
5天前
|
JavaScript
Vue基础知识总结 4:vue组件化开发
Vue基础知识总结 4:vue组件化开发
|
5天前
|
存储 JavaScript
Vue 状态管理工具vuex
Vue 状态管理工具vuex
|
11天前
|
缓存 JavaScript UED
Vue 中实现组件的懒加载
【10月更文挑战第23天】组件的懒加载是 Vue 应用中提高性能的重要手段之一。通过合理运用动态导入、路由配置等方式,可以实现组件的按需加载,减少资源浪费,提高应用的响应速度和用户体验。在实际应用中,需要根据具体情况选择合适的懒加载方式,并结合性能优化的其他措施,以打造更高效、更优质的 Vue 应用。
|
10天前
|
JavaScript
如何在 Vue 中使用具名插槽
【10月更文挑战第25天】通过使用具名插槽,你可以更好地组织和定制组件的模板结构,使组件更具灵活性和可复用性。同时,具名插槽也有助于提高代码的可读性和可维护性。
15 2
|
10天前
|
JavaScript
Vue 中的插槽
【10月更文挑战第25天】插槽的使用可以大大提高组件的复用性和灵活性,使你能够根据具体需求在组件中插入不同的内容,同时保持组件的结构和样式的一致性。
12 2
|
10天前
|
前端开发 JavaScript 容器
在 vite+vue 中使用@originjs/vite-plugin-federation 模块联邦
【10月更文挑战第25天】模块联邦是一种强大的技术,它允许将不同的微前端模块组合在一起,形成一个统一的应用。在 vite+vue 项目中,使用@originjs/vite-plugin-federation 模块联邦可以实现高效的模块共享和组合。通过本文的介绍,相信你已经了解了如何在 vite+vue 项目中使用@originjs/vite-plugin-federation 模块联邦,包括安装、配置和使用等方面。在实际开发中,你可以根据自己的需求和项目的特点,灵活地使用模块联邦,提高项目的可维护性和扩展性。
|
11天前
|
JavaScript 前端开发 UED
vue 提高 tree shaking 的效果
【10月更文挑战第23天】提高 Vue 中 Tree shaking 的效果需要综合考虑多个因素,包括模块的导出和引用方式、打包工具配置、代码结构等。通过不断地优化和调整,可以最大限度地发挥 Tree shaking 的优势,为 Vue 项目带来更好的性能和用户体验。
|
11天前
|
缓存 JavaScript UED
Vue 中异步加载模块的方式
【10月更文挑战第23天】这些异步加载模块的方式各有特点和适用场景,可以根据项目的需求和架构选择合适的方法来实现模块的异步加载,以提高应用的性能和用户体验
|
11天前
|
JavaScript 测试技术 UED
解决 Vue 项目中 Tree shaking 无法去除某些模块
【10月更文挑战第23天】解决 Vue 项目中 Tree shaking 无法去除某些模块的问题需要综合考虑多种因素,通过仔细分析、排查和优化,逐步提高 Tree shaking 的效果,为项目带来更好的性能和用户体验。同时,持续关注和学习相关技术的发展,不断探索新的解决方案,以适应不断变化的项目需求。
|
12天前
|
JavaScript 搜索推荐 前端开发
Vue SSR 预渲染的广泛应用场景及其优势
【10月更文挑战第23天】Vue SSR 预渲染技术在众多领域都有着广泛的应用价值,可以显著提升网站的性能、用户体验和搜索引擎优化效果。随着技术的不断发展和完善,其应用场景还将不断拓展和深化
28 2
下一篇
无影云桌面