前言#
昨天我们分析了runtime入口里的方法,知道了它还带有compile的代码。
坏蛋Dan:vue runtime源码分析学习——day1:入口
但实际上它只是其中一个打包入口。
昨天说过vue的打包文件有两种,一种是runtime-only,也就是只有runtime的代码。另一种则是runtime-compiler,这种打包是带上了compiler-dom以及compiler-core的。
为了能清晰一点,我们来分析下他俩有啥区别。
官方的包#
如果你看过我之前的这篇文章,你应该知道我调试代码是基于vue的core包的。
坏蛋Dan:vue/compiler-sfc源码分析学习1--如何调试
如果没看过建议看一下,这样可以帮助你快速进入调试状态,对于学习源码来说是非常有帮助的。
打包入口#
既然打包输出的文件不同,那么打包的入口应该也是不同的,毕竟我们昨天看的代码中并没有判断是什么方式的逻辑。
我们在拉了core包之后,core包的根目录的package.json里有一个指令build,这个指令会打包这个包里pacakges里的所有包,分别给它们生成对应的dist文件。
代码我们就不看了,感兴趣的大佬可以去看下,在根目录srcripts包中的build.js文件中。
其中它有输出入口文件以及dist之后的文件

呃,绿色的和蓝色的是无关联的,绿色表示文件生成的打印,而蓝色表示文件编译的最终名字。

可以看到我们昨天分析的文件index.ts是作为runtime-compiler的入口
而runtime-only的入口则是同文件夹下的runtime.ts文件。
// This entry exports the runtime only, and is built as
// `dist/vue.esm-bundler.js` which is used by default for bundlers.
import { initDev } from './dev'
import { warn } from '@vue/runtime-dom'
if (__DEV__) {
initDev()
}
export * from '@vue/runtime-dom'
export const compile = () => {
if (__DEV__) {
warn(
`Runtime compilation is not supported in this build of Vue.` +
(__ESM_BUNDLER__
? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".`
: __ESM_BROWSER__
? ` Use "vue.esm-browser.js" instead.`
: __GLOBAL__
? ` Use "vue.global.js" instead.`
: ``) /* should not happen */
)
}
} 可以看到没有compile相关的逻辑,也没有引入compiler-dom,所以打包输出的东西也不会有compiler-dom和compiler-core的代码。
区别#
那么他俩在应用的时候到底有啥不同呢?
我们来创建个场景,一个html直接链入这两种版本。
链接来自:
不同版本的区别#
打包出来的不只有runtime和cjs两个关键词的文件,还有esm-bundler和global等。

在开始之前,这里需要说下几个关键词
prod:发布版本,没有的就是dev版,dev的有错误提示(即__DEV__标志位为true)。runtime:就是runtime-only。cjs:commonJs。用于服务端渲染的。esm:自然就是ES module。global:将vue暴露到全局。有两点需要注意: 1. 这个vue是个单例,所有的api都只能通过它来导入。2. 它是个IIFE

esm-browser:使用方式同上,但是基于ESM,搭配<script type="module">,这样可以使用import导入vue里的api,注意它不是IIFE,因为基于ESM。esm-bundler: 搭配bundler也就是webpack/rollup等。和其他包不同的是,这个包没有dev/prod区别,是否是dev/prod取决于使用者对于process.env.NODE_DEV的定义。

所以结合上面的关键词,我这里列一下:
vue.cjs.js:用于服务端渲染的dev版本。vue.cjs.prod.js:用于服务端渲染的prod版本。vue.global.js:基于IIFE,将Vue单例暴露在全局,api的入口都在这个vue里面。vue.global.prod.js:上面的prod版本。vue.runtime.global.js:同上,但是是runtime-only的版本。vue.runtime.global.prod.js:同上,但是production版本。vue.esm-browser.js:同global,但是是基于ESM的。vue.esm-browser.prod.js:同上,是production版本。vue.runtime.esm-browser.js:同上,但是runtime-only。vue.runtime.esm-browser.prod.js:同上,但是production版本。vue.esm-bundler.js:搭配webapck等bundler,runtime-compiler版本。vue.runtime.esm-bundler.js:同上,但是是runtime-only版本。
具体可看官方文档:core/README.md at main · vuejs/core (github.com)
demo#
由于是直接在html中用,所以不能使用cjs的版本,因为没有bundler,所以也不能用esm-bundler的版本。
所以我们可以选择的只有global或者esm-browser,自然是选择esm-browser。
并且我们是开发阶段,需要错误提示,就不选择prod的版本
创建一个html文件
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.45/vue.esm-browser.js" type="module"></script>
<div id="demo">
</div>
<script type="module">
import { createApp, ref } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.2.45/vue.esm-browser.js'
createApp({
setup () {
const count = ref(0);
return {
count
}
},
template: `
<div class="test" @click="count++">{{ count }}</div>
`
}).mount('#demo')
</script>这个文件中我们可以编译inline template(这里可以把template放到id="demo"的html代码里)

可以看到运行成功了。
而当我们把它切换到runtime.esm-browser.js的时候就会警告并且视图没有正常渲染

那么如果runtime需要使用到类似的效果,该怎么办呢?
这个时候可以使用h函数,h函数会生成VNODE,然后用function包裹,这就是一个render function.
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.45/vue.runtime.esm-browser.js" type="module"></script>
<div id="demo">
</div>
<script type="module">
const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=`
import { createApp, ref, h } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.2.45/vue.runtime.esm-browser.js'
createApp({
setup () {
const count = ref(0);
function handleAdd () {
count.value++;
}
return () => h('div', { class: 'test', onclick: handleAdd }, count.value)
},
}).mount('#demo')
</script>
当然,这个h函数runtime-compiler也是可以用的,因为都包含runtime的代码。
总结#
runtime-only不能处理任何的template模板,而runtime-compiler可以。
他俩的区别在于是否包含compiler-dom和compiler-core这两个包。
参考#
- ^vuejs/core https://github.com/vuejs/core
发布于 2023-01-24 14:48・IP 属地广东
