Przeglądaj źródła

update ssr with tests

Evan You 10 lat temu
rodzic
commit
83ea6ef85d

+ 12 - 2
build/build.js

@@ -21,7 +21,7 @@ var main = fs
   .replace(/Vue\.version = '[\d\.]+'/, "Vue.version = '" + version + "'")
 fs.writeFileSync('src/core/index.js', main)
 
-build([
+var builds = [
   // Runtime only, CommonJS build. Used by bundlers e.g. Webpack & Browserify
   {
     entry: 'src/entries/web-runtime.js',
@@ -70,7 +70,17 @@ build([
     format: 'cjs',
     out: 'dist/server-renderer.js'
   }
-])
+]
+
+// filter builds via command line arg
+if (process.argv[2]) {
+  var filters = process.argv[2].split(',')
+  builds = builds.filter(b => {
+    return filters.some(f => b.out.indexOf(f) > -1)
+  })
+}
+
+build(builds)
 
 function build (builds) {
   var built = 0

+ 18 - 0
build/webpack.ssr.test.config.js

@@ -0,0 +1,18 @@
+var path = require('path')
+var alias = require('./alias')
+
+module.exports = {
+  entry: path.resolve(__dirname, '../test/ssr/ssr.spec.js'),
+  output: {
+    path: path.resolve(__dirname, '../test/ssr'),
+    filename: 'ssr.spec.bundle.js'
+  },
+  resolve: {
+    alias: alias
+  },
+  module: {
+    loaders: [
+      { test: /\.js/, loader: 'babel', exclude: /node_modules/ }
+    ]
+  }
+}

+ 8 - 6
package.json

@@ -11,14 +11,15 @@
   ],
   "scripts": {
     "dev": "webpack --watch --config build/webpack.dist.dev.config.js",
-    "dev-test": "karma start build/karma.dev.config.js",
-    "dev-ssr": "webpack --watch --config build/webpack.ssr.dev.config.js",
-    "test": "npm run lint && npm run unit && npm run e2e",
+    "dev:test": "karma start build/karma.dev.config.js",
+    "dev:ssr": "webpack --watch --config build/webpack.ssr.dev.config.js",
+    "test": "npm run lint && npm run test:unit && npm run test:e2e",
     "build": "NODE_ENV=production node build/build.js",
     "lint": "eslint src build test",
-    "unit": "karma start build/karma.unit.config.js",
-    "cover": "karma start build/karma.cover.config.js",
-    "e2e": "npm run build && node test/e2e/runner.js"
+    "test:unit": "karma start build/karma.unit.config.js",
+    "test:cover": "karma start build/karma.cover.config.js",
+    "test:e2e": "npm run build -- vue.js && node test/e2e/runner.js",
+    "test:ssr": "npm run build -- vue.common.js,compiler.common.js,server-renderer.js && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.json"
   },
   "repository": {
     "type": "git",
@@ -48,6 +49,7 @@
     "eslint-plugin-standard": "^1.3.2",
     "http-server": "^0.9.0",
     "isparta-loader": "^2.0.0",
+    "jasmine": "^2.4.1",
     "jasmine-core": "^2.4.1",
     "karma": "^0.13.15",
     "karma-chrome-launcher": "^0.2.3",

+ 4 - 1
src/compiler/parser/index.js

@@ -347,7 +347,10 @@ function processAttrs (el) {
       // literal attribute
       let expression = parseText(value)
       if (expression) {
-        addAttr(el, name, expression)
+        warn(
+          'Interpolation inside attributes has been deprecated. ' +
+          'Use v-bind or the colon shorthand instead.'
+        )
       } else {
         addStaticAttr(el, name, JSON.stringify(value))
       }

+ 4 - 4
src/entries/web-server-renderer.js

@@ -7,11 +7,11 @@ import { isUnaryTag } from 'web/util/index'
 
 export default createRenderer({
   isUnaryTag,
-  modules: {
+  modules: [
     attrs,
-    style,
-    class: klass
-  },
+    klass,
+    style
+  ],
   directives: {
     show
   }

+ 13 - 1
src/platforms/web/server/modules/attrs.js

@@ -1,9 +1,21 @@
 import { isBooleanAttr, isEnumeratedAttr } from 'web/util/index'
 
 export default function renderAttrs (node) {
-  const attrs = node.data.attrs
+  if (node.data.attrs || node.data.staticAttrs) {
+    return serialize(node.data.staticAttrs) + serialize(node.data.attrs)
+  }
+}
+
+function serialize (attrs) {
   let res = ''
+  if (!attrs) {
+    return res
+  }
   for (let key in attrs) {
+    if (key === 'style') {
+      // leave it to the style module
+      continue
+    }
     if (attrs[key] != null) {
       if (isBooleanAttr(key)) {
         res += ` ${key}="${key}"`

+ 3 - 1
src/platforms/web/server/modules/class.js

@@ -1,5 +1,7 @@
 import { genClassForVnode } from 'web/util/index'
 
 export default function renderClass (node) {
-  return ` class="${genClassForVnode(node)}"`
+  if (node.data.class || node.data.staticClass) {
+    return ` class="${genClassForVnode(node)}"`
+  }
 }

+ 8 - 5
src/platforms/web/server/modules/style.js

@@ -1,10 +1,13 @@
 import { hyphenate } from 'shared/util'
 
 export default function renderStyle (node) {
-  const styles = node.data.style
-  let res = ' style="'
-  for (let key in styles) {
-    res += `${hyphenate(key)}:${styles[key]};`
+  const staticStyle = node.data.staticAttrs && node.data.staticAttrs.style
+  if (node.data.style || staticStyle) {
+    const styles = node.data.style
+    let res = ' style="'
+    for (let key in styles) {
+      res += `${hyphenate(key)}:${styles[key]};`
+    }
+    return res + (staticStyle || '') + '"'
   }
-  return res + '"'
 }

+ 5 - 5
src/server/create-renderer.js

@@ -1,5 +1,5 @@
 export function createRenderer ({
-  modules = {},
+  modules = [],
   directives = {},
   isUnaryTag = (() => false)
 } = {}) {
@@ -47,10 +47,10 @@ export function createRenderer ({
         }
       }
       // apply other modules
-      for (let key in node.data) {
-        let renderer = modules[key]
-        if (renderer) {
-          markup += renderer(node)
+      for (let i = 0; i < modules.length; i++) {
+        let res = modules[i](node)
+        if (res) {
+          markup += res
         }
       }
     }

+ 5 - 0
test/ssr/.eslintrc

@@ -0,0 +1,5 @@
+{
+  "env": {
+    "jasmine": true
+  }
+}

+ 9 - 0
test/ssr/jasmine.json

@@ -0,0 +1,9 @@
+{
+    "spec_dir": "test/ssr",
+    "spec_files": [
+        "ssr.spec.js"
+    ],
+    "helpers": [
+      "../../node_modules/babel-register/lib/node.js"
+    ]
+}

+ 103 - 0
test/ssr/ssr.spec.js

@@ -0,0 +1,103 @@
+import Vue from '../../dist/vue.common.js'
+import { compileToFunctions } from '../../dist/compiler.common.js'
+import { renderToString } from '../../dist/server-renderer.js'
+
+describe('Server side rendering', () => {
+  it('static attributes', () => {
+    expect(renderVmWithOptions({
+      template: '<div id="foo" bar="123"></div>'
+    })).toContain('<div id="foo" bar="123"></div>')
+  })
+
+  it('unary tags', () => {
+    expect(renderVmWithOptions({
+      template: '<input value="123">'
+    })).toContain('<input value="123">')
+  })
+
+  it('dynamic attributes', () => {
+    expect(renderVmWithOptions({
+      template: '<div qux="quux" :id="foo" :bar="baz"></div>',
+      data: {
+        foo: 'hi',
+        baz: 123
+      }
+    })).toContain('<div qux="quux" id="hi" bar="123"></div>')
+  })
+
+  it('static class', () => {
+    expect(renderVmWithOptions({
+      template: '<div class="foo bar"></div>'
+    })).toContain('<div class="foo bar"></div>')
+  })
+
+  it('dynamic class', () => {
+    expect(renderVmWithOptions({
+      template: '<div class="foo bar" :class="[a, { qux: hasQux, quux: hasQuux }]"></div>',
+      data: {
+        a: 'baz',
+        hasQux: true,
+        hasQuux: false
+      }
+    })).toContain('<div class="foo bar baz qux"></div>')
+  })
+
+  it('dynamic style', () => {
+    expect(renderVmWithOptions({
+      template: '<div style="background-color:black" :style="{ fontSize: fontSize + \'px\', color: color }"></div>',
+      data: {
+        fontSize: 14,
+        color: 'red'
+      }
+    })).toContain('<div style="font-size:14px;color:red;background-color:black"></div>')
+  })
+
+  it('text interpolation', () => {
+
+  })
+
+  it('v-if', () => {
+
+  })
+
+  it('v-for', () => {
+
+  })
+
+  it('child component', () => {
+
+  })
+
+  it('everything together', () => {
+    expect(renderVmWithOptions({
+      template: `
+        <div>
+          <p class="hi">yoyo</p>
+          <div id="ho" :class="{ red: isRed }"></div>
+          <span>{{ test }}</span>
+          <test></test>
+        </div>
+      `,
+      data: {
+        test: 'hi',
+        isRed: true
+      },
+      components: {
+        test: {
+          render: function () {
+            return this.$createElement('div', { class: ['a'] }, 'hahahaha')
+          }
+        }
+      }
+    })).toContain('<div><p class="hi">yoyo</p><div id="ho" class="red"></div><span>hi</span><div class="a">hahahaha</div></div>')
+  })
+})
+
+function renderVmWithOptions (options) {
+  const res = compileToFunctions(options.template, {
+    preserveWhitespace: false
+  })
+  Object.assign(options, res)
+  delete options.template
+  return renderToString(new Vue(options))
+}