123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- /* @flow */
- import { isUndef } from 'shared/util'
- type RenderState = {
- type: 'Element';
- rendered: number;
- total: number;
- children: Array<VNode>;
- endTag: string;
- } | {
- type: 'Fragment';
- rendered: number;
- total: number;
- children: Array<VNode>;
- } | {
- type: 'Component';
- prevActive: Component;
- } | {
- type: 'ComponentWithCache';
- buffer: Array<string>;
- bufferIndex: number;
- componentBuffer: Array<Set<Class<Component>>>;
- key: string;
- };
- export class RenderContext {
- userContext: ?Object;
- activeInstance: Component;
- renderStates: Array<RenderState>;
- write: (text: string, next: Function) => void;
- renderNode: (node: VNode, isRoot: boolean, context: RenderContext) => void;
- next: () => void;
- done: (err: ?Error) => void;
- modules: Array<(node: VNode) => ?string>;
- directives: Object;
- isUnaryTag: (tag: string) => boolean;
- cache: any;
- get: ?(key: string, cb: Function) => void;
- has: ?(key: string, cb: Function) => void;
- constructor (options: Object) {
- this.userContext = options.userContext
- this.activeInstance = options.activeInstance
- this.renderStates = []
- this.write = options.write
- this.done = options.done
- this.renderNode = options.renderNode
- this.isUnaryTag = options.isUnaryTag
- this.modules = options.modules
- this.directives = options.directives
- const cache = options.cache
- if (cache && (!cache.get || !cache.set)) {
- throw new Error('renderer cache must implement at least get & set.')
- }
- this.cache = cache
- this.get = cache && normalizeAsync(cache, 'get')
- this.has = cache && normalizeAsync(cache, 'has')
- this.next = this.next.bind(this)
- }
- next () {
- // eslint-disable-next-line
- while (true) {
- const lastState = this.renderStates[this.renderStates.length - 1]
- if (isUndef(lastState)) {
- return this.done()
- }
- /* eslint-disable no-case-declarations */
- switch (lastState.type) {
- case 'Element':
- case 'Fragment':
- const { children, total } = lastState
- const rendered = lastState.rendered++
- if (rendered < total) {
- return this.renderNode(children[rendered], false, this)
- } else {
- this.renderStates.pop()
- if (lastState.type === 'Element') {
- return this.write(lastState.endTag, this.next)
- }
- }
- break
- case 'Component':
- this.renderStates.pop()
- this.activeInstance = lastState.prevActive
- break
- case 'ComponentWithCache':
- this.renderStates.pop()
- const { buffer, bufferIndex, componentBuffer, key } = lastState
- const result = {
- html: buffer[bufferIndex],
- components: componentBuffer[bufferIndex]
- }
- this.cache.set(key, result)
- if (bufferIndex === 0) {
- // this is a top-level cached component,
- // exit caching mode.
- this.write.caching = false
- } else {
- // parent component is also being cached,
- // merge self into parent's result
- buffer[bufferIndex - 1] += result.html
- const prev = componentBuffer[bufferIndex - 1]
- result.components.forEach(c => prev.add(c))
- }
- buffer.length = bufferIndex
- componentBuffer.length = bufferIndex
- break
- }
- }
- }
- }
- function normalizeAsync (cache, method) {
- const fn = cache[method]
- if (isUndef(fn)) {
- return
- } else if (fn.length > 1) {
- return (key, cb) => fn.call(cache, key, cb)
- } else {
- return (key, cb) => cb(fn.call(cache, key))
- }
- }
|