SSR

这是一个SSR实例的踩坑,教程去看vue官网的SSR指南

无意中发现

1
2
3
4
5
6
export function a(){
//这样写才是合法的
}
export a(){
//这样写是会报错的
}

官网上给的示例,记住跳到构建配置,配置client和server。

1
2
3
4
5
6
ctx.response.body = await ( new Promise((resolve,reject)=>{
renderer.renderToString(context,(err,html) =>{
resolve(html);
console.log(html);
})
}) )

构建成功后的项目,会像上面那两张图的样子,难是挺难的,即使有教程(教程对于一些东西写得很迷糊,大概是我的理解能力问题)。我使用的是koa.js 记住要用generator or promise and es7的await(模板你最好也设置下,我是text/plain的,没有<!doctype>会进入怪异模式)。vue-router先不要设置,不然里面是个巨坑,明天再搞,今天太迟了,不过也总算知道SSR的真实面貌了。
02点19分,mark。

服务器渲染的输出结果,你会注意到应用程序的根元素上添加了一个特殊的属性:
1
2
<div id="app" data-server-rendered="true">

= = 可是 问题来了 如何同时让client.json server.json 热加载互相配合koa?

setup-dev-server

这是一个什么样的文件? Link
目的是为了把client.json server.json 在内存中被koa所读取,创建
setupDevServer(app, templatePath, callback) app就是new Koa() ,templatePath就是Index.html (dist那个 创建的话 不要dist那个 要index.template.html 为嘛?webpak常识 = =!),
callback就是createBundleRender。

报错 next is not a function


这个时候果断去看源码啦!下面是根据main:index.js推断代码在middware.js (你看报错内容也行)

1
2
3
4
5
6
7
8
9
10
11
12
function goNext() {
if (!context.options.serverSideRender) {
return next();
}

return new Promise(((resolve) => {
ready(context, () => {
res.locals.webpackStats = context.webpackStats;
resolve(next());
}, req);
}));
}

Koa v2.x 的中间件是基于 Promise 的
所以我需要primise,所以要把context.options.serverSideRender = true。

为什么404 但是返回的内容是正确的?
1
2
3
4
5
6
7
8
9
10
11
12
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-');
this.middleware.push(fn);
return this;
}
debugger; appServer.listen( 8083 ) 打个断点步入看下会使用那些方法。
listen -->
1
2
3
4
5
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
debug -->

/**

  • This is the common logic for both the Node.js and web browser
  • implementations of debug().
    *
  • Expose debug() as the module.
    */
    debug 不深究
callback -->
1
2
3
4
5
6
7
8
9
10
11
12
callback() {
const fn = compose(this.middleware);

if (!this.listenerCount('error')) this.on('error', this.onerror);

const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};

return handleRequest;
}
compose -->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function compose (middleware) {
if (!Array.isArray(middleware)) throw new TypeError(‘Middleware stack must be an array!’)
for (const fn of middleware) {
if (typeof fn !== ‘function’) throw new TypeError(‘Middleware must be composed of functions!’)
}

/*
@param {Object} context
@return {Promise}
@api public
*/

return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error(‘next() called multiple times’))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}

把middleware数组(就是use进去的)封装成fn(ctx,next)

onerror -->
1
2
3
4
5
6
7
8
9
10
11
12
//这是 if (!this.listenerCount('error')) this.on('error', this.onerror); 
onerror(err) {
if (!(err instanceof Error)) throw new TypeError(util.format('non-error thrown: %j', err));

if (404 == err.status || err.expose) return;
if (this.silent) return;

const msg = err.stack || err.toString();
console.error();
console.error(msg.replace(/^/gm, ' '));
console.error();
}
handleRequest -->

1
2
3
4
5
6
7
8
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}

找到了,就是这个把res.statusCode 默认是404。我就推测koa-router模块对于找到的router,会修改statusCode为200吧。
那现在的问题是,devMiddleware帮我找到了文件,但是没有修改res.statusCode。(我没有去使用koa-static是因为这个是在磁盘上的)
在express的兼容性 完全没有问题。

那现在先去查看devmiddle的源码吧

步入middleware.js
res.statusCode = res.statusCode || 200;
看到了吗?中间件处理,因为koa的默认是404,所以现在statusCode是404。
那该怎么解决呢?

这图是一个请求一个静态资源的时候,返回的404。那应该可以推断出koa框架的中间件,当遇不到其他中间件的时候,但是你
依旧使用了await next() 那么就会被设置成404了。好了,那么就强行设置成200吧… 然后又不调用await next()试试,会怎么样?
结果是:浏览器一直在等待。既然我们不熟悉koa的返回正确内容的流畅,那么我们不妨在中间件koa-route上打断点试试?看看koa-route帮
我们设置了什么?哈哈哈,才不要! 这个是个麻瓜的方式,因为,我们这样代表这我们一点都不清楚koa了。
我们使用koa的时候 不用router的时候 是不是 ctx.response.body = ‘Hello World’; 然后就发送正确?嗯,直接发送hellow world到浏览器了。
那么其他文件之类的,只设置body会怎么样?

那么是不是成功了?
慢着,middle.js还为我们设置了字段的

1
2
3
//middle.js (webpack-dev-middle 里面的middle.js)
res.setHeader(‘Content-Type’, contentType);
res.setHeader(‘Content-Length’, content.length);

1
2
3
4
5
6
await devMiddleware(context.req, 
{
send(content){context.response.body = content},
setHeader(…arg){context.set(arguments)}
}
, () => Promise.resolve(‘123‘))

what???神奇的是,文件找成功了!为什么?不是说文件会被设置成404的吗?如果继续调用await next()


果不其然,报错了。
注释掉await next()

OK这下知道了什么回事了!404是因为那个webpack-hot-middleware 更新页面吧(我预感是这样的,因为设置了5second)
思路:我仅仅想获取文件的时候,不调用下一个中间件,而需要匹配路由的内容,就需要await next()
那么我们去写代码吧!

文章目录
  1. 1. 这是一个SSR实例的踩坑,教程去看vue官网的SSR指南
    1. 1.0.1. setup-dev-server
    2. 1.0.2. 那现在先去查看devmiddle的源码吧
|