@15.7.0
.vue
文件分成 template
、script
、style
、custom
模块js
格式文本,带上 type
和 id
等标识后续的解析plugin
更新 module.rules
,通过匹配 resourceQuery
,在最前面加入解析上述模块的内置 loader pitcher
pitcher
的 pitch
函数根据 type
等,再次更新文本调整分发 loader
根据
loader
的pitching
,将loader
放在最前面,并使用pitch
最先执行并返回值不被其他loader
处理 为了 style 模块划出去之后能用到其他 loader,把 lang 后缀加上,在下一轮中 webpack 会把匹配的 rule 都补上
templateLoader
解析 template
模块,转成 render
函数vue-style-loader
的 pitch
函数基于 pitcher
指定的 loader ,根据 RSS、hot 等做调整styleLoader
解析 script
模块,处理 scoped
给节点加上 data-v${id}
属性css-loader
以一般css文件处理app.vue
文件webpack
根据配置,构成loader链vue-loader??vue-loader-options!./app.vue
vue-loader
解析app.vue
文件,返回拆分成多个加载项文本
./app.vue?vue&type=template&id=ed45c92a&
./app.vue?vue&type=script&lang=js&
./app.vue?vue&type=style&index=0&lang=less&
vue-loader.plugin
中,在插件加载时给原rule
增加了?vue&lang=
匹配,在rule
最前面插入匹配?vue
的pitch
webpack
根据配置,构成loader链vue-loader.plugin.pitcher!...langLoaders!vue-loader??vue-loader-options!./app.vue?vue&type=xxx&lang=xxx
vue-loader.plugin.pitcher
做拦截处理;结合‘langLoaders’拼接完整的loader链,直接返回
...postLoaders!templateLoader!...preLoaders!vue-loader??vue-loader-options!./app.vue?type=template&id=ed45c92a&
...loaders!vue-loader??vue-loader-options!./app.vue?type=script&id=ed45c92a&
...afterLoaders!stylePostLoader!...beforeLoaders!vue-loader??vue-loader-options!./app.vue?type=style&id=ed45c92a&index=0&lang=less&
vue-loader
再次解析,针对不同的type
处理返回.vue
文件中不同的部分vue-template-compiler
模版解析返回js,即render方法postcss
解析返回文本解析主模块
字符串查找、正则匹配
transformNode
preTransformNode
postTransformNode
检测到<style>
标签有scoped
属性时,主要做两件事:组件节点增加属性、css选择器都增加属性选择器。
_scopeId
fnScopeId
或直接配置项的_scopeId
做属性@vue/component-compiler-utils/lib/compileStyle.ts
使用postcss
中增加scoped处理插件postcss-selector-parser
给选择器增加一个属性选择器检测到<style>
标签有module
属性时,主要做两件事:构建样式对象、更新css选择器。
beforeCreate
hook,注册$style
(或其他自定义模块名)实例属性vue-style-loader
将css-loader
提供的export.locals
插入到脚本中css-loader
传递了modules
参数css-loader
中postcss
处理css时增加 modules 相关插件
postcss-modules-values
postcss-modules-local-by-default
postcss-modules-extract-imports
postcss-modules-scope
declare type CompilerOptions = {
warn?: Function; // allow customizing warning in different environments; e.g. node
modules?: Array<ModuleOptions>; // platform specific modules; e.g. style; class
directives?: { [key: string]: Function }; // platform specific directives
staticKeys?: string; // a list of AST properties to be considered static; for optimization
isUnaryTag?: (tag: string) => ?boolean; // check if a tag is unary for the platform
canBeLeftOpenTag?: (tag: string) => ?boolean; // check if a tag can be left opened
isReservedTag?: (tag: string) => ?boolean; // check if a tag is a native for the platform
preserveWhitespace?: boolean; // preserve whitespace between elements? (Deprecated)
whitespace?: 'preserve' | 'condense'; // whitespace handling strategy
optimize?: boolean; // optimize static content?
// web specific
mustUseProp?: (tag: string, type: ?string, name: string) => boolean; // check if an attribute should be bound as a property
isPreTag?: (attr: string) => ?boolean; // check if a tag needs to preserve whitespace
getTagNamespace?: (tag: string) => ?string; // check the namespace for a tag
expectHTML?: boolean; // only false for non-web builds
isFromDOM?: boolean;
shouldDecodeTags?: boolean;
shouldDecodeNewlines?: boolean;
shouldDecodeNewlinesForHref?: boolean;
outputSourceRange?: boolean;
// runtime user-configurable
delimiters?: [string, string]; // template delimiters
comments?: boolean; // preserve comments in template
// for ssr optimization compiler
scopeId?: string;
};
declare type WarningMessage = {
msg: string;
start?: number;
end?: number;
};
declare type CompiledResult = {
ast: ?ASTElement;
render: string;
staticRenderFns: Array<string>;
stringRenderFns?: Array<string>;
errors?: Array<string | WarningMessage>;
tips?: Array<string | WarningMessage>;
};
declare type ModuleOptions = {
// transform an AST node before any attributes are processed
// returning an ASTElement from pre/transforms replaces the element
preTransformNode: (el: ASTElement) => ?ASTElement;
// transform an AST node after built-ins like v-if, v-for are processed
transformNode: (el: ASTElement) => ?ASTElement;
// transform an AST node after its children have been processed
// cannot return replacement in postTransform because tree is already finalized
postTransformNode: (el: ASTElement) => void;
genData: (el: ASTElement) => string; // generate extra data string for an element
transformCode?: (el: ASTElement, code: string) => string; // further transform generated code for an element
staticKeys?: Array<string>; // AST properties to be considered static
};
declare type ASTModifiers = { [key: string]: boolean };
declare type ASTIfCondition = { exp: ?string; block: ASTElement };
declare type ASTIfConditions = Array<ASTIfCondition>;
declare type ASTAttr = {
name: string; // 属性名
value: any; // 属性值
dynamic?: boolean;
start?: number;
end?: number
};
declare type ASTElementHandler = {
value: string;
params?: Array<any>;
modifiers: ?ASTModifiers;
dynamic?: boolean;
start?: number;
end?: number;
};
declare type ASTElementHandlers = {
[key: string]: ASTElementHandler | Array<ASTElementHandler>;
};
declare type ASTDirective = {
name: string;
rawName: string;
value: string;
arg: ?string;
isDynamicArg: boolean;
modifiers: ?ASTModifiers;
start?: number;
end?: number;
};
declare type ASTNode = ASTElement | ASTText | ASTExpression;
declare type ASTElement = {
type: 1; // 类型,默认为1,
tag: string; // 标签名,如:‘div’、‘input’
attrsList: Array<ASTAttr>; // 属性列表
attrsMap: { [key: string]: any }; // 以属性名为 key,属性值为 value 的 object
rawAttrsMap: { [key: string]: ASTAttr }; // 以属性名为 key,属性对象为 value 的 object
parent: ASTElement | void; // 父节点
children: Array<ASTNode>; // 子节点列表
start?: number; // 节点的起始在文本的位置
end?: number; // 节点的结束在文本的位置
processed?: true; //?解析结束
static?: boolean;
staticRoot?: boolean;
staticInFor?: boolean;
staticProcessed?: boolean;
hasBindings?: boolean;
text?: string;
attrs?: Array<ASTAttr>; // attrsList 的复制
dynamicAttrs?: Array<ASTAttr>;
props?: Array<ASTAttr>;
plain?: boolean; // !element.key && !element.scopedSlots && !element.attrsList.length
pre?: true; // 是否有‘v-pre’属性,属性列表里会删除,【跳过这个元素和它的子元素的编译过程】
ns?: string; // namespace,有:‘svg’、‘math’
component?: string;
inlineTemplate?: true;
transitionMode?: string | null;
slotName?: ?string;
slotTarget?: ?string;
slotTargetDynamic?: boolean;
slotScope?: ?string;
scopedSlots?: { [name: string]: ASTElement };
ref?: string; // v-ref 表达式
refInFor?: boolean; // 父节点有 for
if?: string; // v-if 属性绑定的表达式
ifProcessed?: boolean;
elseif?: string; // v-else-if 属性绑定的表达式
else?: true; // 有 v-else 属性
ifConditions?: ASTIfConditions; // 条件列表
for?: string; // v-for 属性绑定的 data 值
forProcessed?: boolean;
key?: string; // v-key 属性绑定的表达式
alias?: string; // v-for 属性遍历的 value
iterator1?: string; // v-for 属性遍历的 数组的index,对象的key
iterator2?: string; // v-for 属性遍历的 对象的index
staticClass?: string;
classBinding?: string;
staticStyle?: string;
styleBinding?: string;
events?: ASTElementHandlers;
nativeEvents?: ASTElementHandlers;
transition?: string | true;
transitionOnAppear?: boolean;
model?: {
value: string;
callback: string;
expression: string;
};
directives?: Array<ASTDirective>;
forbidden?: true; // 是否为无效元素,默认没有此字段,有:‘style’、‘script’
once?: true; // 有 v-once 属性
onceProcessed?: boolean;
wrapData?: (code: string) => string;
wrapListeners?: (code: string) => string;
// 2.4 ssr optimization
ssrOptimizability?: number;
// weex specific
appendAsTree?: boolean;
};
declare type ASTExpression = {
type: 2;
expression: string;
text: string;
tokens: Array<string | Object>;
static?: boolean;
// 2.4 ssr optimization
ssrOptimizability?: number;
start?: number;
end?: number;
};
declare type ASTText = {
type: 3;
text: string;
static?: boolean;
isComment?: boolean;
// 2.4 ssr optimization
ssrOptimizability?: number;
start?: number;
end?: number;
};
// SFC-parser related declarations
// an object format describing a single-file component
declare type SFCDescriptor = {
template: ?SFCBlock;
script: ?SFCBlock;
styles: Array<SFCBlock>;
customBlocks: Array<SFCBlock>;
errors: Array<string | WarningMessage>;
}
declare type SFCBlock = {
type: string;
content: string;
attrs: {[attribute:string]: string};
start?: number;
end?: number;
lang?: string;
src?: string;
scoped?: boolean;
module?: string | boolean;
};