增加scpoed特性的支持

pull/1/head
yutent 2022-10-18 16:02:29 +08:00
parent 5e6fc26bd3
commit f5f0cbf1bc
5 changed files with 64 additions and 13 deletions

View File

@ -20,7 +20,7 @@
- 因为没有编译过程, 一切都是基于`vue esm runtime`实时解析, 所以性能会有一定的下降(那点性能损耗一般可以忽略)。 - 因为没有编译过程, 一切都是基于`vue esm runtime`实时解析, 所以性能会有一定的下降(那点性能损耗一般可以忽略)。
- 因为没有打包, 所以所有的文件引用都是按源代码的结构, 对于源码的保护比较弱(虽然打包也没约等于没保护, 因为前端没秘密)。 - 因为没有打包, 所以所有的文件引用都是按源代码的结构, 对于源码的保护比较弱(虽然打包也没约等于没保护, 因为前端没秘密)。
- 因为是用的是原生的`ESM`,所以引用的**依赖/文件**, 需要完整的路径, 不得省略后缀名, 更不能省略`index.js/index.vue`。 - 因为是用的是原生的`ESM`,所以引用的**依赖/文件**, 需要完整的路径, 不得省略后缀名, 更不能省略`index.js/index.vue`。
- 因为没有专门处理样式, 所以原来的`scoped`不可用, 自行使用`命名空间`的方式实现样式隔离; 相应的, vue中的 `>>>、:deep、v-deep`等功能不可用。 - 因为没有内置完整的样式处理, 所以`scoped特性`虽然支持, 但vue中的 `>>>、:deep、v-deep`等功能不可用。
- `单文件组件`中的样式, 如果是用scss, 不支持引用其他文件, 也不支持设置共用定义文件。 - `单文件组件`中的样式, 如果是用scss, 不支持引用其他文件, 也不支持设置共用定义文件。
- 样式预处理器, 只支持scss, 不支持less。 - 样式预处理器, 只支持scss, 不支持less。

View File

@ -6,20 +6,56 @@
import fs from 'iofs' import fs from 'iofs'
import scss from '@bytedo/sass' 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 = { const OPTIONS = {
indentType: 'space', indentType: 'space',
indentWidth: 2 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 * 编译scss为css
* @param file <String> 文件路径或scss代码 * @param file <String> 文件路径或scss代码
* @param style <String> 代码风格, expanded | compressed * @param mini <Boolean> 是否压缩
*/ */
export function compileScss(file, style = 'expanded') { export function compileScss(file, mini = true) {
let style = mini ? 'compressed' : 'expanded'
try { try {
if (fs.isfile(file)) { if (fs.isfile(file)) {
return scss.compile(file, { style, ...OPTIONS }).css 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 html = code.match(HTML_EXP)
let fixedStyle = '\n\n' let fixedStyle = '\n\n'
let hash = md5(file)
// console.log(typeof scss) // console.log(typeof scss)
scss = [...scss].flatMap(it => (it ? it[1] : '')) scss = [...scss].map(it => [it[0], it[1]])
js = js ? js[1] : '' js = js ? js[1] : ''
html = (html ? html[1] : '').replace(/`/g, '\\`').replace(/\$\{/g, '\\${') html = (html ? html[1] : '').replace(/`/g, '\\`').replace(/\$\{/g, '\\${')
html = html.replace(/<([\w\-]+)([^>]*?)>/g, `<$1 data-${hash} $2>`)
js = parseJs(js, imports, options, isBuild).replace( js = parseJs(js, imports, options, isBuild).replace(
'export default {', 'export default {',
@ -129,10 +167,21 @@ export function compileVue(file, imports, options = {}, isBuild) {
) )
if (scss.length) { if (scss.length) {
scss = scss.map(it => {
let scoped = it[0].includes('scoped')
let css = compileScss(it[1])
if (scoped) {
return scopeCss(css, hash)
}
return css
})
js += ` js += `
let stylesheet = new CSSStyleSheet() let stylesheet = new CSSStyleSheet()
stylesheet.path = '${file.slice(options.IS_MPA ? options.pagesDir.length : options.root.length)}' stylesheet.path = '${file.slice(
stylesheet.replaceSync(\`${compileScss(scss.join('\n'))}\`) options.IS_MPA ? options.pagesDir.length : options.root.length
)}'
stylesheet.replaceSync(\`${scss.join('\n')}\`)
document.adoptedStyleSheets.push(stylesheet) document.adoptedStyleSheets.push(stylesheet)
` `
} }

View File

@ -8,6 +8,8 @@ export const JS_EXP = /<script[^>]*?>([\w\W]*?)<\/script>/
export const STYLE_EXP = /<style[^>]*?>([\w\W]*?)<\/style>/g export const STYLE_EXP = /<style[^>]*?>([\w\W]*?)<\/style>/g
export const HTML_EXP = /<template[^>]*?>([\w\W]*?)<\/template>/ export const HTML_EXP = /<template[^>]*?>([\w\W]*?)<\/template>/
export const CSS_SHEET_EXP = /([\w\.,#\-:\(\)\[\]"'\=\s]+)\{([^\{\}]*?)\}/g
export const COMMON_HEADERS = { export const COMMON_HEADERS = {
'Cache-Control': 'no-store' 'Cache-Control': 'no-store'
} }

View File

@ -43,7 +43,7 @@ export default function createServer(root = '', conf = {}) {
currentPage = pageName currentPage = pageName
pagesDir = dirname(conf.pages[pageName].entry) pagesDir = dirname(conf.pages[pageName].entry)
} else { } else {
ext = pathname[pathname.length - 1].split('.').pop() ext = pathname.at(-1).split('.').pop()
pageName = currentPage pageName = currentPage
} }
pathname = pathname.join('/') pathname = pathname.join('/')

View File

@ -1,7 +1,7 @@
{ {
"name": "@bytedo/vue-live", "name": "@bytedo/vue-live",
"type": "module", "type": "module",
"version": "0.0.5", "version": "0.0.6",
"bin": { "bin": {
"vue-live": "index.js" "vue-live": "index.js"
}, },