使用VueJS进行应用开发, 脱离不了对应用间的模块进行拆分, 将大块界面拆解为组件的过程. 我们可以很方便的在单文件中使用 template 块维护组件的视图, 使用 script 维护组件的逻辑部分, 使用 style 维护组件的样式. 在我们编写 VueJS 组件样式时, 不得忽略的一点就是样式污染.
样式污染产生原因
提及样式污染, 主要要追溯到Webpack对CSS文件的打包过程, 这里我们以Vue-Element-Admin中的Webpack配置项举例:
const webpackConfig = merge(baseWebpackConfig, { plugins: [ new MiniCssExtractPlugin({ filename: utils.assetsPath('css/[name].[contenthash:8].css'), chunkFilename: utils.assetsPath('css/[name].[contenthash:8].css')
Webpack 使用 MiniCssExtractPlugin 插件, 将文件(如Vue单文件组件)中的CSS代码, 经过处理后, 分离到形如app.hash1234.css的单独的CSS文件:
如果没有加入防止样式污染的措施的同时, 项目中存在了大量的同名 ClassName, 那么可能会产生意想不到的CSS选择器权重覆盖. 这可能使后文件中某部分选择器权重更高的类影响整个应用, 而此过程通常发生在组件的编写中, 所以一般称之为组件样式污染.
Webpack Vue SFC Object
对于 Vue 项目而言, 使用 Webpack 将极大的优化了工作流程, 因为通过Vue Loader, Vue 单文件组件能很好的融合进 Webpack 工作流中. 通过跟踪源码, 可以发现, 我们写的单文件组件都被处理为了SFC对象, 即包含了单个HTML模块, 单个脚本模块, 一个或多个样式模块, 一个或多个自定义模块的对象:
// vue-loader/index.js const descriptor = parse({ source, compiler: piler || loadTemplateCompiler(), filename, sourceRoot, needMap: sourceMap // piler-utils/index.js function parse(options) { const { compiler } = options output = compiler.parseComponent(source, compilerParseOptions) return output // vue.js function parseComponent(content, options) { // ... var sfc = { template: null, script: null, styles: [], customBlocks: [] // ... return sfc
我们可以将SFC结构融合到Webpack进行开发的过程成中, 主要有这几点影响:
允许为 Vue 组件的每个部分使用其它的 webpack loader,例如在 style 的部分使用 Sass Loader , 在 customBlocks 的部分使用自定义 Loader 使用 webpack loader 将 style 和 template 中引用的资源当作模块依赖来处理 模拟 Scoped CSS 在开发过程中使用热重载来保持状态以下主要介绍Scoped CSS的原理.
Scoped CSS
大白话版本之 Scoped CSS 原理
通过 Webpack 调用 VueJS 中相应 Loader , 给组件HTML模板添加自定义属性 (Attribute) data-v-x, 以及给组件内CSS选择器添加对应的属性选择器 (Attribute Selector) [data-v-x], 达到组件内样式只能生效与组件内HTML的效果, 代码效果如下:
div data-v-lionad /div style .lionad[data-v-lionad] { background: @tiger-orange; /style
源码跟踪
Webpack 使用其它 CSS Loader 处理 VueJS 中对应 CSS 代码之前, Vue Loader 已经替我们做了一层简单的处理, 如果组件中 style 块包含了 scoped 属性:
!-- 某个VueJS组件中 -- template div /div /template style lang="scss" scoped .lionad { background: @tiger-orange; /style
下代码即判断当前SFC对象样式块中是否有scoped属性, 并插入用于 query 中, 顺带一提, 每个单文件组件被解析后, 都会生成对应组件ID, ID主要以生产/开发环境做区分, 通过文件路径+源码或是文件路径的值作为哈希特征值的形式生成, 如下:
// vue-loader/index.js const id = hash(isProduction (shortFilePath + '\n' + source) : shortFilePath) const hasScoped = descriptor.styles.some(s = s.scoped) const query = ` vue type=template${idQuery}${scopedQuery}` const request = templateRequest = stringifyRequest(src + query) templateImport = `import { render, staticRenderFns } from ${request}`
HTML模板处理
在用于处理SFC结构中HTML模板的 templateLoader 中, 我们可以得知, query 中所设置的参数将合并为 loader options 经由 Webpack 转交 templateLoader pileTemplate 处理:
// vue-loader/templateLoader.js const query = qs.parse(this.resourceQuery) const { id } = query pilerOptions = Object.assign({}, pilerOptions, { scopeId: query.scoped `data-v-${id}` : null piled = compileTemplate({ compilerOptions })
代码分别对应:
// piler/build.js/createCompilerCreator var ast = parse(template.trim(), options) optimize(ast, options) var code = generate(ast, options)
先前我们的组件ID在 parse 阶段解析开始标签时就会被推入内部储存的数据结构中:
function elementToOpenTagSegments (el, state) { var segments = [{ type: RAW, value: (" " + (el.tag)) }] // _scopedId if (state.options.scopeId) { segments.push({ type: RAW, value: (" " + (state.options.scopeId)) }) segments.push({ type: RAW, value: " " }) return segments
先前我们的HTML模板 div /div 中开始标签会被转换成如下数据结构:
{ type: RAW, value: ' div' }, { type: RAW, value: ' ' }, { type: RAW, value: 'data-v-xxxxxx' }, { type: RAW, value: ' ' },
样式模板处理
与 HTML Template 解析的过程类似, 通过 Webpack 将样式模板转交 stylePostLoader 进行处理, piler-pileStyle 部分, 后者对样式模板进行解析的过程中, 将会对含 scoped 标记的模板引入插件 stylePlugins/scoped.js, scoped.js 将 data-v-xxxxxx 添加到选择器末尾的过程如下:
selectors.each((selector) = { selector.each((n) = { if (n.value === '::v-deep' || n.value === ' ' || n.value === '/deep/') { return false; selector.insertAfter(node, selectorParser.attribute({ attribute: id
题外话, 通过以上代码, 我们发现当当前处理到三种特定类型选择器会终止循环, 停止将 data-v-xxx 添加到选择器末尾:
伪类 ::v-deep 选择器 选择器 /deep/我们可以利用这个特征, 在组件中写样式穿透, 即内部组件影响外部组件样式 (ε=ε=ε=┏(゜ロ゜;)┛ 主动样式污染), 当然这在特定的情境下是有用的, 比如当我们想主动覆盖第三方UI组件框架的样式, 却不想引入新的CSS文件, 或不想写非 Scoped CSS 模板的时候.
最后
本人前端菜得捉急, 文中不详尽或有错的地方, 欢迎各位大佬斧正. 如果本文对你有所帮助, 那是再好不过, 看到这里都是真爱啊
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持凡科。
深层次探寻VueJS Scoped CSS 完成基本原理 -Morotar 本文关键详细……
vue完成Input键入框模糊不清查寻方式 ,远方有止 本文关键……
多元化化企业集团,其网站必须与公众新闻媒体,上级领导领导……
公司建网站为何都找找中强互联网建网站创立于二零一一年河南……
【手机微信微信小程序制作】微信小程序开发设计的限定 手机……
复荣团体是一家技术专业的卫浴洁具家居家具用具企业集团。自……