l林州住房建设部官方网站,电商网站数据中心建设方案,机械设备企业网站源码,wordpress查询码文章目录 一、背景二、源码分析transformMain 返回值transformStyle 方法compileStyleAsync 方法scopedPlugin 方法template 添加 __scopeId 三、总结 一、背景
Vue 3 文件编译流程详解与 Babel 的使用
上文分析了 vue3 的编译过程#xff0c;但是在对其中样式的解析遗留了一… 文章目录 一、背景二、源码分析transformMain 返回值transformStyle 方法compileStyleAsync 方法scopedPlugin 方法template 添加 __scopeId 三、总结 一、背景
Vue 3 文件编译流程详解与 Babel 的使用
上文分析了 vue3 的编译过程但是在对其中样式的解析遗留了一些问题:
为什么 genStyleCode 得到了 import 语句 我们真正的代码是怎么转化的平时vue scoped是怎么实现样式隔离的如下图标签/选择器上的唯一属性怎么加上去的 带着这些疑问继续进行源码解析。
二、源码分析
书接上回我们发现了 transformMain 方法中 genStyleCode 会处理我们的为import /Users/zcy/Desktop/毕设/smart-port/src/App.vue?vuetypestyleindex0lang.less 那是怎么翻译成具体的 css 的呢 transformMain 返回值
我们先看一下 transformMain 方法把转码转化为了什么? 下面是转化后格式化完成后的代码
// 源码script部分
import { ref } from vue; const _sfc_main {setup(__props, { expose }) {expose(); const state ref(1)const __returned__ { state, ref }Object.defineProperty(__returned__, __isScriptSetup, { enumerable: false, value: true })return __returned__}
}
import {resolveComponent as _resolveComponent, createVNode as _createVNode, toDisplayString as _toDisplayString,createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock,createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId
} from vue
const _withScopeId n (_pushScopeId(data-v-7ba5bd90), n n(), _popScopeId(), n)
const _hoisted_1 { id: nav }
// 源码template部分
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {const _component_router_link _resolveComponent(router-link)const _component_router_view _resolveComponent(router-view)return (_openBlock(),_createElementBlock(_Fragment, null,[_createElementVNode(div, _hoisted_1,[_createVNode(_component_router_link, { to: /login }),_createVNode(_component_router_link, { to: / }),_createElementVNode(div, null, _toDisplayString($setup.state), 1 /* TEXT */)]),_createVNode(_component_router_view)], 64 /* STABLE_FRAGMENT */))
}// 源码样式部分
import /Users/zcy/Desktop/毕设/smart-port/src/App.vue?vuetypestyleindex0scopedtruelang.less_sfc_main.__hmrId 7ba5bd90
typeof __VUE_HMR_RUNTIME__ ! undefined __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main)
import.meta.hot.accept(({ default: updated, _rerender_only }) { if (_rerender_only) { __VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render) } else { __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated) } })
import _export_sfc from plugin-vue:export-helper
export default /*#__PURE__*/_export_sfc(_sfc_main, [[render, _sfc_render], [__scopeId, data-v-7ba5bd90], [__file, /Users/zcy/Desktop/毕设/smart-port/src/App.vue]])上面这段代码核心三部分 _sfc_render、 styles_sfc_main可以看到这里对样式的解析其实只转化为了一个 import 方法那是怎么会转化真正的 css 的呢 这个时候我们在回到 vuePlugin 入口处的 transform 方法中如下图 transformStyle 方法
从上图中可以看在 vue 不存在的时候会进入 transformMain 否则会进入到 else 中 transformStyle 方法从名字就可以看出这个转化样式的过程因此我们放开 transformMain 的断点在 transformStyle 打上断点并进入该方法。 compileStyleAsync 方法
进入到改方法后我们可以看到内部在执行了 compileStyleAsync 方法, 之后样式就已经加上了隔离由 #app - #app[data-v-7ba5bd90] 因此我们深入一下 options.compiler.compileStyleAsync 这个方法根据我们上一篇文得知这个方法是在 vue/compiler-sfc 核心包中我们打个断点进入该方法。 compileStyleAsync 执行了 doCompileStyle 接下来我简单摘要一下这个方法
function doCompileStyle(options) {const { filename, id, scoped false, trim true, isProd false, modules false, modulesOptions {}, preprocessLang, postcssOptions, postcssPlugins } options;// scoped id 来自于 descriptor.idconst shortId id.replace(/^data-v-/, );const longId data-v-${shortId};// 插件数组const plugins (postcssPlugins || []).slice();plugins.unshift(cssVarsPlugin({ id: shortId, isProd }));// scoped 存在则加入改插件if (scoped) {plugins.push(scopedPlugin(longId));}let result;let code;try {// postcss 处理这些插件result _postcss__default(plugins).process(source, postCSSOptions);// In async mode, return a promise.if (options.isAsync) {return result.then(result ({code: result.css || ,// xxx}))}code result.css;}return {code: code || ,map: outMap outMap.toJSON(),// xxxx};
}scopedPlugin 方法
这边可以看到使用了 postcss 加载各种插件其中就有 scopedPlugin 顾名思义就是给我添加样式隔离的接下来我们进入这个方法看一眼 scopedPlugin 中的 Rule 配置会调用 processRule 方法并传入 scopedIdprocessRule 方法会遍历每个选择器进行执行 rewriteSelector 操作rewriteSelector 方法会处理 v-deep、 、 /deep/、等特殊操作符最后加上 给选择器加上 scopedId 属性
如上图所示会在该方法中对 css 选择器加上隔离 __scopeId
template 添加 __scopeId
选择器的样式加上了 那我们 template 中的属性什么时候加上呢 回到我们最开始 transformMain 中 返回的 code 当中我们可以看到源码的 template 转化为了 render 函数并且传入了 __scopeId 我们不难猜到肯定会在 调用 _createElementBlock 等方法的时候会转化为 vdom最后更新到 dom 属性中, 对于 vdom 的转化过程本文就不过多深入。
三、总结
从上文可以发现 vue3 对 css 的解析其实是分为两次
第一次先通过 transformMain 得到 import xxxx.vue?xxxx 的方法第二次因为新增了import 语句插件又会重新执行再次执行因为 vue 已经存在了则会进入 transformStyle 方法在里面进行具体解析包含 scoped 等各种插件配合使用解析为最终 css 文件。