loader源码分析CSS Scoped的实现(2)

因此,为vue单文件组件中每个标签执行的lang属性,也可以应用在webpack配置同样后缀的rule。这种设计就可以保证在不侵入vue-loader的情况下,为每个标签配置独立的loader,如

可以使用pug编写template,然后配置pug-plain-loader

可以使用scss或less编写style,然后配置相关预处理器loader

可见在VueLoaderPlugin主要做的两件事,一个是注册公共的pitcher,一个是复制webpack的rules。

vue-loader

接下来我们看看vue-loader做的事情。

pitcher

前面提到在VueLoaderPlugin中,该loader在pitch中会根据query.type注入处理对应标签的loader

当type为style时,在css-loader后插入stylePostLoader,保证stylePostLoader在execution阶段先执行

当type为template时,插入templateLoader

// pitcher.js module.exports = code => code module.exports.pitch = function (remainingRequest) { if (query.type === `style`) { // 会查询cssLoaderIndex并将其放在afterLoaders中 // loader在execution阶段是从后向前执行的 const request = genRequest([ ...afterLoaders, stylePostLoaderPath, // 执行lib/loaders/stylePostLoader.js ...beforeLoaders ]) return `import mod from ${request}; export default mod; export * from ${request}` } // 处理模板 if (query.type === `template`) { const preLoaders = loaders.filter(isPreLoader) const postLoaders = loaders.filter(isPostLoader) const request = genRequest([ ...cacheLoader, ...postLoaders, templateLoaderPath + `??vue-loader-options`, // 执行lib/loaders/templateLoader.js ...preLoaders ]) return `export * from ${request}` } // ... }

由于loader.pitch会先于loader,在捕获阶段执行,因此主要进行上面的准备工作:检查query.type并直接调用相关的loader

type=style,执行stylePostLoader

type=template,执行templateLoader

这两个loader的具体作用我们后面再研究。

vueLoader

接下来看看vue-loader里面做的工作,当引入一个x.vue文件时

// vue-loader/lib/index.js 下面source为Vue代码文件原始内容 // 将单个*.vue文件内容解析成一个descriptor对象,也称为SFC(Single-File Components)对象 // descriptor包含template、script、style等标签的属性和内容,方便为每种标签做对应处理 const descriptor = parse({ source, compiler: options.compiler || loadTemplateCompiler(loaderContext), filename, sourceRoot, needMap: sourceMap }) // 为单文件组件生成唯一哈希id const id = hash( isProduction ? (shortFilePath + '\n' + source) : shortFilePath ) // 如果某个style标签包含scoped属性,则需要进行CSS Scoped处理,这也是本章节需要研究的地方 const hasScoped = descriptor.styles.some(s => s.scoped)

处理template标签,拼接type=template等query参数

if (descriptor.template) { const src = descriptor.template.src || resourcePath const idQuery = `&id=${id}` // 传入文件id和scoped=true,在为组件的每个HTML标签传入组件id时需要这两个参数 const scopedQuery = hasScoped ? `&scoped=true` : `` const attrsQuery = attrsToQuery(descriptor.template.attrs) const query = `?vue&type=template${idQuery}${scopedQuery}${attrsQuery}${inheritQuery}` const request = templateRequest = stringifyRequest(src + query) // type=template的文件会传给templateLoader处理 templateImport = `import { render, staticRenderFns } from ${request}` // 比如,<template lang="pug"></template>标签 // 将被解析成 import { render, staticRenderFns } from "./source.vue?vue&type=template&id=27e4e96e&lang=pug&" }

处理script标签

let scriptImport = `var script = {}` if (descriptor.script) { // vue-loader没有对script做过多的处理 // 比如vue文件中的<script></script>标签将被解析成 // import script from "./source.vue?vue&type=script&lang=js&" // export * from "./source.vue?vue&type=script&lang=js&" }

处理style标签,为每个标签拼接type=style等参数

// 在genStylesCode中,会处理css scoped和css moudle stylesCode = genStylesCode( loaderContext, descriptor.styles, id, resourcePath, stringifyRequest, needsHotReload, isServer || isShadow // needs explicit injection? ) // 由于一个vue文件里面可能存在多个style标签,对于每个标签,将调用genStyleRequest生成对应文件的依赖 function genStyleRequest (style, i) { const src = style.src || resourcePath const attrsQuery = attrsToQuery(style.attrs, 'css') const inheritQuery = `&${loaderContext.resourceQuery.slice(1)}` const idQuery = style.scoped ? `&id=${id}` : `` // type=style将传给stylePostLoader进行处理 const query = `?vue&type=style&index=${i}${idQuery}${attrsQuery}${inheritQuery}` return stringifyRequest(src + query) }

可见在vue-loader中,主要是将整个文件按照标签拼接对应的query路径,然后交给webpack按顺序调用相关的loader。

templateLoader

回到开头提到的第一个问题:当前组件中,渲染出来的每个HTML标签中的hash属性是如何生成的。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wjfsfz.html