sourceMap = false,
filename = `template.vue.html`,
scopeId = null,
+ optimizeBindings = false,
runtimeGlobalName = `Vue`,
runtimeModuleName = `vue`,
ssr = false
sourceMap,
filename,
scopeId,
+ optimizeBindings,
runtimeGlobalName,
runtimeModuleName,
ssr,
indentLevel: 0,
map: undefined,
helper(key) {
- const name = helperNameMap[key]
- return prefixIdentifiers ? name : `_${name}`
+ return `_${helperNameMap[key]}`
},
push(code, node) {
context.code += code
function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
const {
ssr,
- helper,
prefixIdentifiers,
push,
newline,
!__BROWSER__ && ssr
? `require(${JSON.stringify(runtimeModuleName)})`
: runtimeGlobalName
+ const aliasHelper = (s: symbol) => `${helperNameMap[s]}: _${helperNameMap[s]}`
// Generate const declaration for helpers
// In prefix mode, we place the const declaration at top so it's done
// only once; But if we not prefixing, we place the declaration inside the
// with block so it doesn't incur the `in` check cost for every helper access.
if (ast.helpers.length > 0) {
if (!__BROWSER__ && prefixIdentifiers) {
- push(`const { ${ast.helpers.map(helper).join(', ')} } = ${VueBinding}\n`)
+ push(
+ `const { ${ast.helpers.map(aliasHelper).join(', ')} } = ${VueBinding}\n`
+ )
} else {
// "with" mode.
// save Vue in a separate variable to avoid collision
if (ast.hoists.length) {
const staticHelpers = [CREATE_VNODE, CREATE_COMMENT, CREATE_TEXT]
.filter(helper => ast.helpers.includes(helper))
- .map(s => `${helperNameMap[s]}: _${helperNameMap[s]}`)
+ .map(aliasHelper)
.join(', ')
push(`const { ${staticHelpers} } = _Vue\n`)
}
// ssr guaruntees prefixIdentifier: true
push(
`const { ${ast.ssrHelpers
- .map(helper)
+ .map(aliasHelper)
.join(', ')} } = require("@vue/server-renderer")\n`
)
}
context: CodegenContext,
genScopeId: boolean
) {
- const { push, helper, newline, scopeId, runtimeModuleName } = context
+ const {
+ push,
+ helper,
+ newline,
+ scopeId,
+ optimizeBindings,
+ runtimeModuleName
+ } = context
if (genScopeId) {
ast.helpers.push(WITH_SCOPE_ID)
// generate import statements for helpers
if (ast.helpers.length) {
- push(
- `import { ${ast.helpers.map(helper).join(', ')} } from ${JSON.stringify(
- runtimeModuleName
- )}\n`
- )
+ if (optimizeBindings) {
+ // when bundled with webpack with code-split, calling an import binding
+ // as a function leads to it being wrapped with `Object(a.b)` or `(0,a.b)`,
+ // incurring both payload size increase and potential perf overhead.
+ // therefore we assign the imports to vairables (which is a constant ~50b
+ // cost per-component instead of scaling with template size)
+ push(
+ `import { ${ast.helpers
+ .map(s => helperNameMap[s])
+ .join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
+ )
+ push(
+ `\n// Binding optimization for webpack code-split\nconst ${ast.helpers
+ .map(s => `_${helperNameMap[s]} = ${helperNameMap[s]}`)
+ .join(', ')}\n`
+ )
+ } else {
+ push(
+ `import { ${ast.helpers
+ .map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
+ .join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
+ )
+ }
}
if (ast.ssrHelpers && ast.ssrHelpers.length) {
push(
`import { ${ast.ssrHelpers
- .map(helper)
+ .map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
.join(', ')} } from "@vue/server-renderer"\n`
)
}
scopeId?: string | null
// we need to know about this to generate proper preambles
prefixIdentifiers?: boolean
+ // option to optimize helper import bindings via variable assignment
+ // (only used for webpack code-split)
+ optimizeBindings?: boolean
// for specifying where to import helpers
runtimeModuleName?: string
runtimeGlobalName?: string
export const SSR_GET_DYNAMIC_MODEL_PROPS = Symbol(`ssrGetDynamicModelProps`)
export const ssrHelpers = {
- [SSR_INTERPOLATE]: `_ssrInterpolate`,
- [SSR_RENDER_COMPONENT]: `_ssrRenderComponent`,
- [SSR_RENDER_SLOT]: `_ssrRenderSlot`,
- [SSR_RENDER_CLASS]: `_ssrRenderClass`,
- [SSR_RENDER_STYLE]: `_ssrRenderStyle`,
- [SSR_RENDER_ATTRS]: `_ssrRenderAttrs`,
- [SSR_RENDER_ATTR]: `_ssrRenderAttr`,
- [SSR_RENDER_DYNAMIC_ATTR]: `_ssrRenderDynamicAttr`,
- [SSR_RENDER_LIST]: `_ssrRenderList`,
- [SSR_LOOSE_EQUAL]: `_ssrLooseEqual`,
- [SSR_LOOSE_CONTAIN]: `_ssrLooseContain`,
- [SSR_RENDER_DYNAMIC_MODEL]: `_ssrRenderDynamicModel`,
- [SSR_GET_DYNAMIC_MODEL_PROPS]: `_ssrGetDynamicModelProps`
+ [SSR_INTERPOLATE]: `ssrInterpolate`,
+ [SSR_RENDER_COMPONENT]: `ssrRenderComponent`,
+ [SSR_RENDER_SLOT]: `ssrRenderSlot`,
+ [SSR_RENDER_CLASS]: `ssrRenderClass`,
+ [SSR_RENDER_STYLE]: `ssrRenderStyle`,
+ [SSR_RENDER_ATTRS]: `ssrRenderAttrs`,
+ [SSR_RENDER_ATTR]: `ssrRenderAttr`,
+ [SSR_RENDER_DYNAMIC_ATTR]: `ssrRenderDynamicAttr`,
+ [SSR_RENDER_LIST]: `ssrRenderList`,
+ [SSR_LOOSE_EQUAL]: `ssrLooseEqual`,
+ [SSR_LOOSE_CONTAIN]: `ssrLooseContain`,
+ [SSR_RENDER_DYNAMIC_MODEL]: `ssrRenderDynamicModel`,
+ [SSR_GET_DYNAMIC_MODEL_PROPS]: `ssrGetDynamicModelProps`
}
// Note: these are helpers imported from @vue/server-renderer
export { renderToString } from './renderToString'
// internal runtime helpers
-export { renderComponent as _ssrRenderComponent } from './renderToString'
-export { ssrRenderSlot as _ssrRenderSlot } from './helpers/ssrRenderSlot'
+export { renderComponent } from './renderToString'
+export { ssrRenderSlot } from './helpers/ssrRenderSlot'
export {
- ssrRenderClass as _ssrRenderClass,
- ssrRenderStyle as _ssrRenderStyle,
- ssrRenderAttrs as _ssrRenderAttrs,
- ssrRenderAttr as _ssrRenderAttr,
- ssrRenderDynamicAttr as _ssrRenderDynamicAttr
+ ssrRenderClass,
+ ssrRenderStyle,
+ ssrRenderAttrs,
+ ssrRenderAttr,
+ ssrRenderDynamicAttr
} from './helpers/ssrRenderAttrs'
-export { ssrInterpolate as _ssrInterpolate } from './helpers/ssrInterpolate'
-export { ssrRenderList as _ssrRenderList } from './helpers/ssrRenderList'
+export { ssrInterpolate } from './helpers/ssrInterpolate'
+export { ssrRenderList } from './helpers/ssrRenderList'
// v-model helpers
export {
- ssrLooseEqual as _ssrLooseEqual,
- ssrLooseContain as _ssrLooseContain,
- ssrRenderDynamicModel as _ssrRenderDynamicModel,
- ssrGetDynamicModelProps as _ssrGetDynamicModelProps
+ ssrLooseEqual,
+ ssrLooseContain,
+ ssrRenderDynamicModel,
+ ssrGetDynamicModelProps
} from './helpers/ssrVModelHelpers'
export const compilerOptions: CompilerOptions = reactive({
mode: 'module',
prefixIdentifiers: false,
+ optimizeBindings: false,
hoistStatic: false,
cacheHandlers: false,
scopeId: null
},
`@${__COMMIT__}`
),
+ ' | ',
+ h(
+ 'a',
+ {
+ href:
+ 'https://app.netlify.com/sites/vue-next-template-explorer/deploys',
+ target: `_blank`
+ },
+ 'History'
+ ),
+
+ h('div', { id: 'options-wrapper' }, [
+ h('div', { id: 'options-label' }, 'Options ↘'),
+ h('ul', { id: 'options' }, [
+ // mode selection
+ h('li', { id: 'mode' }, [
+ h('span', { class: 'label' }, 'Mode: '),
+ h('input', {
+ type: 'radio',
+ id: 'mode-module',
+ name: 'mode',
+ checked: isModule,
+ onChange() {
+ compilerOptions.mode = 'module'
+ }
+ }),
+ h('label', { for: 'mode-module' }, 'module'),
+ ' ',
+ h('input', {
+ type: 'radio',
+ id: 'mode-function',
+ name: 'mode',
+ checked: !isModule,
+ onChange() {
+ compilerOptions.mode = 'function'
+ }
+ }),
+ h('label', { for: 'mode-function' }, 'function')
+ ]),
- h('div', { id: 'options' }, [
- // mode selection
- h('span', { class: 'options-group' }, [
- h('span', { class: 'label' }, 'Mode:'),
- h('input', {
- type: 'radio',
- id: 'mode-module',
- name: 'mode',
- checked: isModule,
- onChange() {
- compilerOptions.mode = 'module'
- }
- }),
- h('label', { for: 'mode-module' }, 'module'),
- h('input', {
- type: 'radio',
- id: 'mode-function',
- name: 'mode',
- checked: !isModule,
- onChange() {
- compilerOptions.mode = 'function'
- }
- }),
- h('label', { for: 'mode-function' }, 'function')
- ]),
+ // SSR
+ h('li', [
+ h('input', {
+ type: 'checkbox',
+ id: 'ssr',
+ name: 'ssr',
+ checked: ssrMode.value,
+ onChange(e: Event) {
+ ssrMode.value = (e.target as HTMLInputElement).checked
+ }
+ }),
+ h('label', { for: 'ssr' }, 'SSR')
+ ]),
- // SSR
- h('input', {
- type: 'checkbox',
- id: 'ssr',
- name: 'ssr',
- checked: ssrMode.value,
- onChange(e: Event) {
- ssrMode.value = (e.target as HTMLInputElement).checked
- }
- }),
- h('label', { for: 'ssr' }, 'SSR'),
+ // toggle prefixIdentifiers
+ h('li', [
+ h('input', {
+ type: 'checkbox',
+ id: 'prefix',
+ disabled: isModule || isSSR,
+ checked: usePrefix || isSSR,
+ onChange(e: Event) {
+ compilerOptions.prefixIdentifiers =
+ (e.target as HTMLInputElement).checked || isModule
+ }
+ }),
+ h('label', { for: 'prefix' }, 'prefixIdentifiers')
+ ]),
- // toggle prefixIdentifiers
- h('input', {
- type: 'checkbox',
- id: 'prefix',
- disabled: isModule || isSSR,
- checked: usePrefix || isSSR,
- onChange(e: Event) {
- compilerOptions.prefixIdentifiers =
- (e.target as HTMLInputElement).checked || isModule
- }
- }),
- h('label', { for: 'prefix' }, 'prefixIdentifiers'),
+ // toggle hoistStatic
+ h('li', [
+ h('input', {
+ type: 'checkbox',
+ id: 'hoist',
+ checked: compilerOptions.hoistStatic && !isSSR,
+ disabled: isSSR,
+ onChange(e: Event) {
+ compilerOptions.hoistStatic = (e.target as HTMLInputElement).checked
+ }
+ }),
+ h('label', { for: 'hoist' }, 'hoistStatic')
+ ]),
- // toggle hoistStatic
- h('input', {
- type: 'checkbox',
- id: 'hoist',
- checked: compilerOptions.hoistStatic && !isSSR,
- disabled: isSSR,
- onChange(e: Event) {
- compilerOptions.hoistStatic = (e.target as HTMLInputElement).checked
- }
- }),
- h('label', { for: 'hoist' }, 'hoistStatic'),
+ // toggle cacheHandlers
+ h('li', [
+ h('input', {
+ type: 'checkbox',
+ id: 'cache',
+ checked: usePrefix && compilerOptions.cacheHandlers && !isSSR,
+ disabled: !usePrefix || isSSR,
+ onChange(e: Event) {
+ compilerOptions.cacheHandlers = (e.target as HTMLInputElement).checked
+ }
+ }),
+ h('label', { for: 'cache' }, 'cacheHandlers')
+ ]),
- // toggle cacheHandlers
- h('input', {
- type: 'checkbox',
- id: 'cache',
- checked: usePrefix && compilerOptions.cacheHandlers && !isSSR,
- disabled: !usePrefix || isSSR,
- onChange(e: Event) {
- compilerOptions.cacheHandlers = (e.target as HTMLInputElement).checked
- }
- }),
- h('label', { for: 'cache' }, 'cacheHandlers'),
+ // toggle scopeId
+ h('li', [
+ h('input', {
+ type: 'checkbox',
+ id: 'scope-id',
+ disabled: !isModule,
+ checked: isModule && compilerOptions.scopeId,
+ onChange(e: Event) {
+ compilerOptions.scopeId =
+ isModule && (e.target as HTMLInputElement).checked
+ ? 'scope-id'
+ : null
+ }
+ }),
+ h('label', { for: 'scope-id' }, 'scopeId')
+ ]),
- // toggle scopeId
- h('input', {
- type: 'checkbox',
- id: 'scope-id',
- disabled: !isModule,
- checked: isModule && compilerOptions.scopeId,
- onChange(e: Event) {
- compilerOptions.scopeId =
- isModule && (e.target as HTMLInputElement).checked
- ? 'scope-id'
- : null
- }
- }),
- h('label', { for: 'scope-id' }, 'scopeId')
+ // toggle optimizeBindings
+ h('li', [
+ h('input', {
+ type: 'checkbox',
+ id: 'optimize-bindings',
+ disabled: !isModule || isSSR,
+ checked: isModule && !isSSR && compilerOptions.optimizeBindings,
+ onChange(e: Event) {
+ compilerOptions.optimizeBindings = (e.target as HTMLInputElement).checked
+ }
+ }),
+ h('label', { for: 'optimize-bindings' }, 'optimizeBindings')
+ ])
+ ])
])
]
}
border-bottom: 1px solid #333;
padding: 0.3em 1.6em;
color: #fff;
+ z-index: 1;
}
h1 {
margin-right: 15px;
}
-#options {
- float: right;
- margin-top: 1em;
+#options-wrapper {
+ position: absolute;
+ top: 20px;
+ right: 10px;
}
-.options-group {
- margin-right: 30px;
+#options-wrapper:hover #options {
+ display: block;
}
-#header span, #header label, #header input, #header a {
- display: inline-block;
+#options-label {
+ cursor: pointer;
+ text-align: right;
+ padding-right: 10px;
+ font-weight: bold;
+}
+
+#options {
+ display: none;
+ margin-top: 15px;
+ list-style-type: none;
+ background-color: #1e1e1e;
+ border: 1px solid #333;
+ padding: 15px 30px;
+}
+
+#options li {
+ margin: 8px 0;
}
#header a {
}
#header input {
- margin-left: 12px;
margin-right: 6px;
}