Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

render.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /* @flow */
  2. import { escape } from 'web/server/util'
  3. import { SSR_ATTR } from 'shared/constants'
  4. import { RenderContext } from './render-context'
  5. import { generateComponentTrace } from 'core/util/debug'
  6. import { ssrCompileToFunctions } from 'web/server/compiler'
  7. import { installSSRHelpers } from './optimizing-compiler/runtime-helpers'
  8. import { isDef, isUndef, isTrue } from 'shared/util'
  9. import {
  10. createComponent,
  11. createComponentInstanceForVnode
  12. } from 'core/vdom/create-component'
  13. let warned = Object.create(null)
  14. const warnOnce = msg => {
  15. if (!warned[msg]) {
  16. warned[msg] = true
  17. console.warn(`\n\u001b[31m${msg}\u001b[39m\n`)
  18. }
  19. }
  20. const onCompilationError = (err, vm) => {
  21. const trace = vm ? generateComponentTrace(vm) : ''
  22. throw new Error(`\n\u001b[31m${err}${trace}\u001b[39m\n`)
  23. }
  24. const normalizeRender = vm => {
  25. const { render, template, _scopeId } = vm.$options
  26. if (isUndef(render)) {
  27. if (template) {
  28. const compiled = ssrCompileToFunctions(template, {
  29. scopeId: _scopeId,
  30. warn: onCompilationError
  31. }, vm)
  32. vm.$options.render = compiled.render
  33. vm.$options.staticRenderFns = compiled.staticRenderFns
  34. } else {
  35. throw new Error(
  36. `render function or template not defined in component: ${
  37. vm.$options.name || vm.$options._componentTag || 'anonymous'
  38. }`
  39. )
  40. }
  41. }
  42. }
  43. function renderNode (node, isRoot, context) {
  44. if (node.isString) {
  45. renderStringNode(node, context)
  46. } else if (isDef(node.componentOptions)) {
  47. renderComponent(node, isRoot, context)
  48. } else if (isDef(node.tag)) {
  49. renderElement(node, isRoot, context)
  50. } else if (isTrue(node.isComment)) {
  51. if (isDef(node.asyncFactory)) {
  52. // async component
  53. renderAsyncComponent(node, isRoot, context)
  54. } else {
  55. context.write(`<!--${node.text}-->`, context.next)
  56. }
  57. } else {
  58. context.write(
  59. node.raw ? node.text : escape(String(node.text)),
  60. context.next
  61. )
  62. }
  63. }
  64. function registerComponentForCache (options, write) {
  65. // exposed by vue-loader, need to call this if cache hit because
  66. // component lifecycle hooks will not be called.
  67. const register = options._ssrRegister
  68. if (write.caching && isDef(register)) {
  69. write.componentBuffer[write.componentBuffer.length - 1].add(register)
  70. }
  71. return register
  72. }
  73. function renderComponent (node, isRoot, context) {
  74. const { write, next, userContext } = context
  75. // check cache hit
  76. const Ctor = node.componentOptions.Ctor
  77. const getKey = Ctor.options.serverCacheKey
  78. const name = Ctor.options.name
  79. const cache = context.cache
  80. const registerComponent = registerComponentForCache(Ctor.options, write)
  81. if (isDef(getKey) && isDef(cache) && isDef(name)) {
  82. const key = name + '::' + getKey(node.componentOptions.propsData)
  83. const { has, get } = context
  84. if (isDef(has)) {
  85. has(key, hit => {
  86. if (hit === true && isDef(get)) {
  87. get(key, res => {
  88. if (isDef(registerComponent)) {
  89. registerComponent(userContext)
  90. }
  91. res.components.forEach(register => register(userContext))
  92. write(res.html, next)
  93. })
  94. } else {
  95. renderComponentWithCache(node, isRoot, key, context)
  96. }
  97. })
  98. } else if (isDef(get)) {
  99. get(key, res => {
  100. if (isDef(res)) {
  101. if (isDef(registerComponent)) {
  102. registerComponent(userContext)
  103. }
  104. res.components.forEach(register => register(userContext))
  105. write(res.html, next)
  106. } else {
  107. renderComponentWithCache(node, isRoot, key, context)
  108. }
  109. })
  110. }
  111. } else {
  112. if (isDef(getKey) && isUndef(cache)) {
  113. warnOnce(
  114. `[vue-server-renderer] Component ${
  115. Ctor.options.name || '(anonymous)'
  116. } implemented serverCacheKey, ` +
  117. 'but no cache was provided to the renderer.'
  118. )
  119. }
  120. if (isDef(getKey) && isUndef(name)) {
  121. warnOnce(
  122. `[vue-server-renderer] Components that implement "serverCacheKey" ` +
  123. `must also define a unique "name" option.`
  124. )
  125. }
  126. renderComponentInner(node, isRoot, context)
  127. }
  128. }
  129. function renderComponentWithCache (node, isRoot, key, context) {
  130. const write = context.write
  131. write.caching = true
  132. const buffer = write.cacheBuffer
  133. const bufferIndex = buffer.push('') - 1
  134. const componentBuffer = write.componentBuffer
  135. componentBuffer.push(new Set())
  136. context.renderStates.push({
  137. type: 'ComponentWithCache',
  138. key,
  139. buffer,
  140. bufferIndex,
  141. componentBuffer
  142. })
  143. renderComponentInner(node, isRoot, context)
  144. }
  145. function renderComponentInner (node, isRoot, context) {
  146. const prevActive = context.activeInstance
  147. // expose userContext on vnode
  148. node.ssrContext = context.userContext
  149. const child = context.activeInstance = createComponentInstanceForVnode(
  150. node,
  151. context.activeInstance
  152. )
  153. normalizeRender(child)
  154. const childNode = child._render()
  155. childNode.parent = node
  156. context.renderStates.push({
  157. type: 'Component',
  158. prevActive
  159. })
  160. renderNode(childNode, isRoot, context)
  161. }
  162. function renderAsyncComponent (node, isRoot, context) {
  163. const factory = node.asyncFactory
  164. const resolve = comp => {
  165. if (comp.__esModule && comp.default) {
  166. comp = comp.default
  167. }
  168. const { data, children, tag } = node.asyncMeta
  169. const nodeContext = node.asyncMeta.context
  170. const resolvedNode: any = createComponent(
  171. comp,
  172. data,
  173. nodeContext,
  174. children,
  175. tag
  176. )
  177. if (resolvedNode) {
  178. if (resolvedNode.componentOptions) {
  179. // normal component
  180. renderComponent(resolvedNode, isRoot, context)
  181. } else if (!Array.isArray(resolvedNode)) {
  182. // single return node from functional component
  183. renderNode(resolvedNode, isRoot, context)
  184. } else {
  185. // multiple return nodes from functional component
  186. context.renderStates.push({
  187. type: 'Fragment',
  188. children: resolvedNode,
  189. rendered: 0,
  190. total: resolvedNode.length
  191. })
  192. context.next()
  193. }
  194. } else {
  195. // invalid component, but this does not throw on the client
  196. // so render empty comment node
  197. context.write(`<!---->`, context.next)
  198. }
  199. }
  200. if (factory.resolved) {
  201. resolve(factory.resolved)
  202. return
  203. }
  204. const reject = context.done
  205. let res
  206. try {
  207. res = factory(resolve, reject)
  208. } catch (e) {
  209. reject(e)
  210. }
  211. if (res) {
  212. if (typeof res.then === 'function') {
  213. res.then(resolve, reject).catch(reject)
  214. } else {
  215. // new syntax in 2.3
  216. const comp = res.component
  217. if (comp && typeof comp.then === 'function') {
  218. comp.then(resolve, reject).catch(reject)
  219. }
  220. }
  221. }
  222. }
  223. function renderStringNode (el, context) {
  224. const { write, next } = context
  225. if (isUndef(el.children) || el.children.length === 0) {
  226. write(el.open + (el.close || ''), next)
  227. } else {
  228. const children: Array<VNode> = el.children
  229. context.renderStates.push({
  230. type: 'Element',
  231. children,
  232. rendered: 0,
  233. total: children.length,
  234. endTag: el.close
  235. })
  236. write(el.open, next)
  237. }
  238. }
  239. function renderElement (el, isRoot, context) {
  240. const { write, next } = context
  241. if (isTrue(isRoot)) {
  242. if (!el.data) el.data = {}
  243. if (!el.data.attrs) el.data.attrs = {}
  244. el.data.attrs[SSR_ATTR] = 'true'
  245. }
  246. if (el.fnOptions) {
  247. registerComponentForCache(el.fnOptions, write)
  248. }
  249. const startTag = renderStartingTag(el, context)
  250. const endTag = `</${el.tag}>`
  251. if (context.isUnaryTag(el.tag)) {
  252. write(startTag, next)
  253. } else if (isUndef(el.children) || el.children.length === 0) {
  254. write(startTag + endTag, next)
  255. } else {
  256. const children: Array<VNode> = el.children
  257. context.renderStates.push({
  258. type: 'Element',
  259. children,
  260. rendered: 0,
  261. total: children.length,
  262. endTag
  263. })
  264. write(startTag, next)
  265. }
  266. }
  267. function hasAncestorData (node: VNode) {
  268. const parentNode = node.parent
  269. return isDef(parentNode) && (isDef(parentNode.data) || hasAncestorData(parentNode))
  270. }
  271. function getVShowDirectiveInfo (node: VNode): ?VNodeDirective {
  272. let dir: VNodeDirective
  273. let tmp
  274. while (isDef(node)) {
  275. if (node.data && node.data.directives) {
  276. tmp = node.data.directives.find(dir => dir.name === 'show')
  277. if (tmp) {
  278. dir = tmp
  279. }
  280. }
  281. node = node.parent
  282. }
  283. return dir
  284. }
  285. function renderStartingTag (node: VNode, context) {
  286. let markup = `<${node.tag}`
  287. const { directives, modules } = context
  288. // construct synthetic data for module processing
  289. // because modules like style also produce code by parent VNode data
  290. if (isUndef(node.data) && hasAncestorData(node)) {
  291. node.data = {}
  292. }
  293. if (isDef(node.data)) {
  294. // check directives
  295. const dirs = node.data.directives
  296. if (dirs) {
  297. for (let i = 0; i < dirs.length; i++) {
  298. const name = dirs[i].name
  299. const dirRenderer = directives[name]
  300. if (dirRenderer && name !== 'show') {
  301. // directives mutate the node's data
  302. // which then gets rendered by modules
  303. dirRenderer(node, dirs[i])
  304. }
  305. }
  306. }
  307. // v-show directive needs to be merged from parent to child
  308. const vshowDirectiveInfo = getVShowDirectiveInfo(node)
  309. if (vshowDirectiveInfo) {
  310. directives.show(node, vshowDirectiveInfo)
  311. }
  312. // apply other modules
  313. for (let i = 0; i < modules.length; i++) {
  314. const res = modules[i](node)
  315. if (res) {
  316. markup += res
  317. }
  318. }
  319. }
  320. // attach scoped CSS ID
  321. let scopeId
  322. const activeInstance = context.activeInstance
  323. if (isDef(activeInstance) &&
  324. activeInstance !== node.context &&
  325. isDef(scopeId = activeInstance.$options._scopeId)
  326. ) {
  327. markup += ` ${(scopeId: any)}`
  328. }
  329. if (isDef(node.fnScopeId)) {
  330. markup += ` ${node.fnScopeId}`
  331. } else {
  332. while (isDef(node)) {
  333. if (isDef(scopeId = node.context.$options._scopeId)) {
  334. markup += ` ${scopeId}`
  335. }
  336. node = node.parent
  337. }
  338. }
  339. return markup + '>'
  340. }
  341. export function createRenderFunction (
  342. modules: Array<(node: VNode) => ?string>,
  343. directives: Object,
  344. isUnaryTag: Function,
  345. cache: any
  346. ) {
  347. return function render (
  348. component: Component,
  349. write: (text: string, next: Function) => void,
  350. userContext: ?Object,
  351. done: Function
  352. ) {
  353. warned = Object.create(null)
  354. const context = new RenderContext({
  355. activeInstance: component,
  356. userContext,
  357. write, done, renderNode,
  358. isUnaryTag, modules, directives,
  359. cache
  360. })
  361. installSSRHelpers(component)
  362. normalizeRender(component)
  363. renderNode(component._render(), true, context)
  364. }
  365. }