diff --git a/Readme.md b/Readme.md index a4614df..23c254b 100644 --- a/Readme.md +++ b/Readme.md @@ -20,7 +20,7 @@ - 因为没有编译过程, 一切都是基于`vue esm runtime`实时解析, 所以性能会有一定的下降(那点性能损耗一般可以忽略)。 - 因为没有打包, 所以所有的文件引用都是按源代码的结构, 对于源码的保护比较弱(虽然打包也没约等于没保护, 因为前端没秘密)。 - 因为是用的是原生的`ESM`,所以引用的**依赖/文件**, 需要完整的路径, 不得省略后缀名, 更不能省略`index.js/index.vue`。 -- 因为没有专门处理样式, 所以原来的`scoped`不可用, 自行使用`命名空间`的方式实现样式隔离; 相应的, vue中的 `>>>、:deep、v-deep`等功能不可用。 +- 因为没有内置完整的样式处理, 所以`scoped特性`虽然支持, 但vue中的 `>>>、:deep、v-deep`等功能不可用。 - `单文件组件`中的样式, 如果是用scss, 不支持引用其他文件, 也不支持设置共用定义文件。 - 样式预处理器, 只支持scss, 不支持less。 diff --git a/lib/compile-vue.js b/lib/compile-vue.js index 2664939..106a672 100644 --- a/lib/compile-vue.js +++ b/lib/compile-vue.js @@ -6,20 +6,56 @@ import fs from 'iofs' import scss from '@bytedo/sass' +import { createHash } from 'crypto' -import { JS_EXP, STYLE_EXP, HTML_EXP } from './constants.js' +import { JS_EXP, STYLE_EXP, HTML_EXP, CSS_SHEET_EXP } from './constants.js' const OPTIONS = { indentType: 'space', indentWidth: 2 } +function md5(str = '') { + let sum = createHash('md5') + sum.update(str, 'utf8') + return sum.digest('hex').slice(0, 8) +} + +function scopeCss(css, hash) { + let rules = css.matchAll(CSS_SHEET_EXP) + + return [...rules] + .map(r => { + let selector = r[1] + let style = r[2] + selector = selector.split(',') + + selector = selector + .map(s => { + let tmp = s.split(' ') + let last = tmp.pop() + if (last.includes(':')) { + last = last.replace(':', `[data-${hash}]:`) + } else { + last += `[data-${hash}]` + } + tmp.push(last) + return tmp.join(' ') + }) + .join(', ') + + return selector + ` {${style}}` + }) + .join('\n') +} + /** * 编译scss为css * @param file 文件路径或scss代码 - * @param style 代码风格, expanded | compressed + * @param mini 是否压缩 */ -export function compileScss(file, style = 'expanded') { +export function compileScss(file, mini = true) { + let style = mini ? 'compressed' : 'expanded' try { if (fs.isfile(file)) { return scss.compile(file, { style, ...OPTIONS }).css @@ -117,11 +153,13 @@ export function compileVue(file, imports, options = {}, isBuild) { let html = code.match(HTML_EXP) let fixedStyle = '\n\n' + let hash = md5(file) // console.log(typeof scss) - scss = [...scss].flatMap(it => (it ? it[1] : '')) + scss = [...scss].map(it => [it[0], it[1]]) js = js ? js[1] : '' html = (html ? html[1] : '').replace(/`/g, '\\`').replace(/\$\{/g, '\\${') + html = html.replace(/<([\w\-]+)([^>]*?)>/g, `<$1 data-${hash} $2>`) js = parseJs(js, imports, options, isBuild).replace( 'export default {', @@ -129,12 +167,23 @@ export function compileVue(file, imports, options = {}, isBuild) { ) if (scss.length) { - js += ` - let stylesheet = new CSSStyleSheet() - stylesheet.path = '${file.slice(options.IS_MPA ? options.pagesDir.length : options.root.length)}' - stylesheet.replaceSync(\`${compileScss(scss.join('\n'))}\`) - document.adoptedStyleSheets.push(stylesheet) - ` + scss = scss.map(it => { + let scoped = it[0].includes('scoped') + let css = compileScss(it[1]) + if (scoped) { + return scopeCss(css, hash) + } + return css + }) + + js += ` + let stylesheet = new CSSStyleSheet() + stylesheet.path = '${file.slice( + options.IS_MPA ? options.pagesDir.length : options.root.length + )}' + stylesheet.replaceSync(\`${scss.join('\n')}\`) + document.adoptedStyleSheets.push(stylesheet) + ` } return js diff --git a/lib/constants.js b/lib/constants.js index 8b350f0..b56dae9 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -8,6 +8,8 @@ export const JS_EXP = /]*?>([\w\W]*?)<\/script>/ export const STYLE_EXP = /]*?>([\w\W]*?)<\/style>/g export const HTML_EXP = /]*?>([\w\W]*?)<\/template>/ +export const CSS_SHEET_EXP = /([\w\.,#\-:\(\)\[\]"'\=\s]+)\{([^\{\}]*?)\}/g + export const COMMON_HEADERS = { 'Cache-Control': 'no-store' } diff --git a/lib/dev.js b/lib/dev.js index f57468c..92a060e 100644 --- a/lib/dev.js +++ b/lib/dev.js @@ -43,7 +43,7 @@ export default function createServer(root = '', conf = {}) { currentPage = pageName pagesDir = dirname(conf.pages[pageName].entry) } else { - ext = pathname[pathname.length - 1].split('.').pop() + ext = pathname.at(-1).split('.').pop() pageName = currentPage } pathname = pathname.join('/') diff --git a/package.json b/package.json index 97dab1d..cb91232 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@bytedo/vue-live", "type": "module", - "version": "0.0.5", + "version": "0.0.6", "bin": { "vue-live": "index.js" },