

Vue不同编译输出文件的区别
source link: https://segmentfault.com/a/1190000010640595
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Vue不同编译输出文件的区别
Vue
源码是选用了rollup
作为bundler
,看Vue
的源码时发现:npm script
对应了不同的构建选项。这也对应了最后打包构建后产出的不同的包。
不同于其他的library
,Vue
为什么要在最后的打包构建环节输出不同类型的包呢?接下来我们通过Vue
的源码以及对应的构建配置中简单的去分析下。
由于Vue
是基于rollup进行构建的,我们先来简单了解下rollup
这个bundler
:rollup
是默认使用ES Module
规范而非CommonJS
,因此如果你在你的项目中使用rollup
作为构建工具的话,那么可以放心的使用ES Module
规范,但是如果要引入只遵循了CommonJs
规范的第三包的话,还需要使用相关的插件,插件会帮你将CommonJs
规范的代码转为ES Module
。得益于ES Module
,rollup
在构建前进行静态分析,进行tree-shaking
。关于tree-shaking
的描述请戳我。在构建输出环节,rollup
提供了多种文件输出类型:
iife
: 立即执行函数cjs
: 遵循CommonJs Module
规范的文件输出amd
: 遵循AMD Module
规范的文件输出umd
: 支持外链
/CommonJs Module
/AMD Module
规范的文件输出es
: 将多个遵循ES6 Module
的文件编译成1个ES6 Module
接下来我们就看看Vue
的使用rollup
进行构建的几个不同的版本(使用于browser
的版本)。
npm run dev 对应
rollup -w -c build/config.js --environment TARGET:web-full-dev
rollup
对应的配置信息为:
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: resolve('web/runtime-with-compiler.js'),
dest: resolve('dist/vue.js'),
format: 'umd',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
开发环境下输出的umd
格式的代码,入口文件是runtime-with-compiler.js
,这个入口文件中是将Vue
的构建时
和运行时
的代码都统一进行打包了,通过查看这个入口文件,我们注意到
...
import { compileToFunctions } from './compiler/index'
...
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function () {
}
我们发现,这个文件当中,首先将原来定义的Vue.prototype.$mount
方法缓存起来,然后将这个方法进行重写,重写后的方法当中,首先判断是否有自定义的render
函数,如果有自定义的render
函数的话,Vue
不会通过自带的compiler
对模板进行编译并生成render
函数。但是如果没有自定义的render
函数,那么会调用compiler
对你定义的模板进行编译,并生成render
函数,所以通过这个rollup
的配置构建出来的代码既支持自定义render
函数,又支持template
模板编译:
// 将模板编译成render函数,并挂载到vm实例的options属性上
const { render, staticRenderFns } = compileToFunctions(template, {
shouldDecodeNewlines,
delimiters: options.delimiters
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
...
// 调用之前缓存的mount函数,TODO: 关于这个函数里面发生了什么请戳我
return mount.call(this, el, hydrating)
接下来看第二种构建方式:
npm run dev:cjs 对应的构建脚本
rollup -w -c build/config.js --environment TARGET:web-runtime-cjs
rollup
对应的配置信息为:
// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
'web-runtime-cjs': {
entry: resolve('web/runtime.js'),
dest: resolve('dist/vue.runtime.common.js'),
format: 'cjs',
banner
}
最后编译输出的文件是遵循CommonJs Module
同时只包含runtime
部分的代码,它能直接被webpack 1.x
和Browserify
直接load
。它对应的入口文件是runtime.js
:
import Vue from './runtime/index'
export default Vue
这里没有重写Vue.prototye.$mount
方法,因此在vm
实例的生命周期中,进行到beforeMount
阶段时:
// vm挂载的根元素
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
// vm.$el为真实的node
vm.$el = el
// 如果vm上没有挂载render函数
if (!vm.$options.render) {
// 空节点
vm.$options.render = createEmptyVNode
if (process.env.NODE_ENV !== 'production') {
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el || el) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
)
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
)
}
}
}
// 钩子函数
callHook(vm, 'beforeMount')
let updateComponent
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = () => {
const name = vm._name
const id = vm._uid
const startTag = `vue-perf-start:${id}`
const endTag = `vue-perf-end:${id}`
mark(startTag)
const vnode = vm._render()
mark(endTag)
measure(`${name} render`, startTag, endTag)
mark(startTag)
vm._update(vnode, hydrating)
mark(endTag)
measure(`${name} patch`, startTag, endTag)
}
} else {
// updateComponent为监听函数, new Watcher(vm, updateComponent, noop)
updateComponent = () => {
// Vue.prototype._render 渲染函数
// vm._render() 返回一个VNode
// 更新dom
// vm._render()调用render函数,会返回一个VNode,在生成VNode的过程中,会动态计算getter,同时推入到dep里面
// 在非ssr情况下hydrating为false
vm._update(vm._render(), hydrating)
}
}
// 新建一个_watcher对象
// vm实例上挂载的_watcher主要是为了更新DOM
// 在实例化watcher的过程中,就会执行updateComponent,完成对依赖的变量的收集过程
// vm/expression/cb
vm._watcher = new Watcher(vm, updateComponent, noop)
hydrating = false
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
首先判断vm
实例上是否定义了render
函数。如果没有,那么就会新建一个新的空vnode
并挂载到render
函数上。此外,如果页面的渲染是通过传入根节点
的形式:
new Vue({
el: '#app'
})
Vue
便会打出log
信息:
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.'
意思就是你当前使用的是只包含runtime
打包后的代码,模板的编译器(即构建时)的代码并不包含在里面。因此,你不能通过挂根节点或者是声明式模板的方式去组织你的html
内容,而只能使用render
函数去书写模板内容。不过报错信息里面也给出了提示信息就是,你还可以选择pre-compile
预编译工具去将template
模板编译成render
函数(vue-loader
就起到了这个作用)或者是使用包含了compiler
的输出包,也就是上面分析的即包含compiler
,又包含runtime
的包。
第三种构建方式:
npm run dev:esm 对应的构建脚本为:
rollup -w -c build/config.js --environment TARGET:web-runtime-esm
入口文件及最后构建出来的代码内容和第二种一样,只包含runtime
部分的代码,但是输出代码是遵循ES Module
规范的。可以被支持ES Module
的bundler
直接加载,如webpack2
和rollup
。
第四种构建方式:
npm run dev:compiler 对应的构建脚本为:
rollup -w -c build/config.js --environment TARGET:web-compiler
不同于前面3种构建方式:
// Web compiler (CommonJS).
'web-compiler': {
entry: resolve('web/compiler.js'),
dest: resolve('packages/vue-template-compiler/build.js'),
format: 'cjs',
external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)
},
这一构建对应于将关于Vue
模板编译的成render
函数的compiler.js
单独进行打包输出。最后输出的packages/vue-template-compiler/build.js
文件会单独作为一个node_modules
进行发布,在你的开发过程中,如果使用了webpack
作为构建工具,以及vue-loader
,在开发构建环节,vue-loader
便会通过web compiler
去处理你的*.vue
文件中的模板<template>
当中的内容,将这些模板字符串编译为render
函数。
Recommend
-
67
这篇的东西比较多。 首先要处理一下 inside-out/aux 和 inside-out 这两个函数。之前的 inside-out/aux 其实一直不支持对 progn 的处理,需要先补充;而 inside-...
-
7
Aug 13, 2019 • 付林 齐江 df与du输出结果不同的原因研究 Question 标准GNU工具coreutils中有俩程序df / du,他们都可以查看磁盘的使用情况。通常情况下他们的统计结果并不会相同,这是因为统计信息来源的差异。所以问题来了:...
-
33
hardhat solidity不同版本,不能正常编译 hardhat solidity不同版本,不能正常编译 ...
-
4
炎忍的博客解决 VS Code 编译 C/C++ 是输出中文乱码的问题发表于2020-04-08|更新于2020-12-14|技术记录
-
10
V2EX › C++ 不同版本的 visual studio 编译出来的 dll,可以混用吗? byaiu · 6 小时 4 分钟前 · 703 次...
-
6
V2EX › C++ 请教 cmake 中如何兼容不同的编译环境 Yc1992 · 17 小时 42 分钟前 · 631 次点击
-
5
在实际软件项目开发中,项目需要经过不同环境验证通过后才能上线,而每个环境都有不同的配置参数,如何管理这些不同环境的配置数据对于项目开发和部署均至关重要。 在 Apache Maven 中是可以实现这...
-
6
编译一日一练(DIY系列之汇编输出)
-
6
Linux下Make编译结果输出到文件
-
5
如何高雅的让ChatGPT输出不同的写作风格? 作者:d'k 2023-06-09 00:11:57 人工智能 修改文风变化, 在输入问题时加上:use temperature of 0...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK