手动传入组件类进行-render

使用编译产出的组件类存在的问题

在 ssr 过程中,模板可能需要读取组件实例上的属性或方法,因此我们需要在每次渲染时创建组件的一个实例。

当编译输出字符串时,会有以下问题:

  • 当输入为 JavaScript 或 TypeScript 时,San-SSR 会将组件类的字符串定义直接输出在产物中,之后在渲染阶段根据这些类创建组件实例。当组件书写方式过于复杂时,解析可能会失败。
  • 当输入为 Class 时,San-SSR 会尝试将内存中的类字符串化。该方法只能输出组件自身内容,如果组件类引用了外部变量等,执行阶段会报错。

编译输出为 render 函数时,则可以避免上述问题。

基于以上,San-SSR 提供了在执行阶段手动传入组件 Class 进行渲染的能力,此时编译产物中不会含有组件类代码,San-SSR 的产物与组件代码需要一起上到线上执行。

使用方式

编译阶段

const MyComponent = require('./component')
const sanProject = new SanProject()
const res = sanProject.compileToSource(MyComponent, 'js', {
    useProvidedComponentClass: true
})

fs.writeFileSync(path.resolve(__dirname, './output.js'), res)

线上执行

只需传入根组件即可

const Component = require('./component')
const render = require('./output')

const html = render({}, { ComponentClass: Component })

与 markExternalComponent 特性配合使用

当与 markExternalComponent 配合使用时,由于定义外部组件引用的 specifier 为组件类文件,我们需要使用 customSSRFilePath 来帮助 San-SSR 找到外部组件所对应的 San-SSR 产物位置:

const Component = require('./component')
const render = require('./output')

const html = render({}, {
    ComponentClass: Component,
    parentCtx: {
        context: {
            customSSRFilePath ({ specifier }) {
                if (specifier.endsWith('childA.san')) {
                    return specifier + '.ssr'
                }
            }
        }
    }
})

如果我们想要完全改变子组件的路径,则可以再配合 customComponentFilePath 方法,替换所使用的子组件类:

const Component = require('./component')
const render = require('./output')

const html = render({}, {
    ComponentClass: Component,
    parentCtx: {
        context: {
            customSSRFilePath ({ specifier, id, tagName }) {
                if (specifier.endsWith('childA.san')) {
                    return specifier + '.ssr'
                }
            },
            customComponentFilePath ({ specifier, id, tagName }) {
                if (specifier === './childA.san') {
                    if (tagName === 'x-b') {
                        return path.resolve(__dirname, './childB.san')
                    }
                    return path.resolve(__dirname, './childA.san')
                }
            },
        }
    }
})