xinpianchang / fe-weekly

weekly for fe-team
10 stars 2 forks source link

vue3 之 compiler-sfc #89

Open dailynodejs opened 3 years ago

dailynodejs commented 3 years ago

源码地址:https://github.com/vuejs/vue-next/tree/master/packages/compiler-sfc

                                  +--------------------+
                                  |                    |
                                  |  script transform  |
                           +----->+                    |
                           |      +--------------------+
                           |
+--------------------+     |      +--------------------+
|                    |     |      |                    |
|  facade transform  +----------->+ template transform |
|                    |     |      |                    |
+--------------------+     |      +--------------------+
                           |
                           |      +--------------------+
                           +----->+                    |
                                  |  style transform   |
                                  |                    |
                                  +--------------------+

facade module looks like this:

// main script
import script from '/project/foo.vue?vue&type=script'
// template compiled to render function
import { render } from '/project/foo.vue?vue&type=template&id=xxxxxx'
// css
import '/project/foo.vue?vue&type=style&index=0&id=xxxxxx'

// attach render function to script
script.render = render

// attach additional metadata
// some of these should be dev only
script.__file = 'example.vue'
script.__scopeId = 'xxxxxx'

// additional tooling-specific HMR handling code
// using __VUE_HMR_API__ global

export default script
dailynodejs commented 3 years ago

处理 style

https://github.com/vuejs/vue-next/blob/master/packages/compiler-sfc/src/stylePreprocessors.ts

内置了预编译

export type PreprocessLang = 'less' | 'sass' | 'scss' | 'styl' | 'stylus'

export type StylePreprocessor = {
  source: string,
  map: RawSourceMap | undefined,
  options: {
    [key: string]: any,
    additionalData?: string | ((source: string, filename: string) => string),
    filename: string
  },
  customRequire: SFCStyleCompileOptions['preprocessCustomRequire']
} => StylePreprocessorResults

export const processors: Record<PreprocessLang, StylePreprocessor> = {
  less,
  sass,
  scss,
  styl,
  stylus: styl
}

通用方法

function getSource(
  source: string,
  filename: string,
  additionalData?: string | ((source: string, filename: string) => string)
) {
  if (!additionalData) return source
  if (isFunction(additionalData)) {
    return additionalData(source, filename)
  }
  return additionalData + source
}

less

const less: StylePreprocessor = (source, map, options, load = require) => {
  const nodeLess = load('less')
  // ...
}

parseCssVars

https://github.com/vuejs/vue-next/blob/master/packages/compiler-sfc/src/cssVars.ts#L35

export const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g

export function parseCssVars(sfc: SFCDescriptor): string[] {
  const vars: string[] = []
  sfc.styles.forEach(style => {
    let match
    const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '')
    while ((match = cssVarRe.exec(content))) {
      const variable = match[1] || match[2] || match[3]
      if (!vars.includes(variable)) {
        vars.push(variable)
      }
    }
  })
}

genCssVarsCode

源码地址:https://github.com/vuejs/vue-next/blob/master/packages/compiler-sfc/src/cssVars.ts#L73

export function genCssVarsCode(
  vars: string[],
  bindings: BindingMetadata,
  id: string,
  isProd: boolean
)  {
  const varsExp = genCssVarsFromList(vars, id, isProd)
  const exp = createSimpleExpression(varsExp, false)
  const context = createTransformContext(createRoot([]), {
    prefixIdentifiers: true,
    inline: true,
    bindingMetadata: bindings.__isScriptSetup === false ? undefined: bindings
  })
}
dailynodejs commented 3 years ago

sfc style variables

https://github.com/vuejs/rfcs/blob/master/active-rfcs/0043-sfc-style-variables.md

.login-tips {
    font-size: 12px;
    line-height: 30px;
    color: v-bind(color);
}

image

父容器:

image

多个的话:

父容器:

image

image

对应的 style

image

对象

image

转换成 font_size

image

dailynodejs commented 3 years ago

useCssVars

genNormalScriptCssVarsCode

源码:https://github.com/vuejs/vue-next/blob/master/packages/compiler-sfc/src/cssVars.ts#L103

export const CSS_VARS_HELPER = `useCssVars`
export function genNormalScriptCssVarsCode(
  cssVars: string[],
  bindings: BindingMetadata,
  id: string,
  isProd: boolean
): string {
  return (
    `\nimport { ${CSS_VARS_HELPER} as _${CSS_VARS_HELPER} } from 'vue'\n` + 
    `const __injectCSSVars__ = () => {\n${genCssVarsCode(
        cssVars,
        bindings,
        id,
        isProd
     )}}\n` + 
     `const __setup__ = __default__.setup\n` +
      `__default__.setup = __setup__\n` + 
      `  ? (props, ctx) => { __injectCSSVars__(); return __setup__(props, ctx) }\n` + 
      `  : __injectCSSVars__\n`
  )
}

编译之后:

import { useCssVars as _useCssVars } from '/node_modules/.vite/vue.js?v=e62380f6'
const __injectCSSVars__ = () => {
_useCssVars(_ctx => ({
  "26084dc2-font_size": (_ctx.font.size),
  "26084dc2-color": (_ctx.color)
}))}

https://github.com/vuejs/vue-next/blob/master/packages/runtime-dom/src/helpers/useCssVars.ts#L17

image

父元素是如何加上 style 的

function setVarsOnNode(el: Node, vars: Record<string, string>) {
  if (el.nodeType === 1) {
    const style = (el as HTMLElement).style
    for (const key in vars) {
      style.setProperty(`--${key}`,  vars[key])
    }
  }
}

setVarsOnVNode

image

image

dailynodejs commented 3 years ago

https://blog.csdn.net/weixin_43487782/article/details/117701178

dailynodejs commented 3 years ago

genVarName

key 生成的规则 https://github.com/vuejs/vue-next/blob/master/packages/compiler-sfc/src/cssVars.ts#L27

上面提到的 - 变 _

用到了工具包 hash-sum:

https://github.com/bevacqua/hash-sum

import hash from 'hash-sum'
function genVarName(id: string, raw: string, isProd: boolean): string {
  if (isProd) {
    return hash(id + raw)
  } else {
    return `${id}-${raw.replace(/([^\w-])/g, '_')}`
  }
}