vue-ssr(下)
本次主要说一下,ssr怎么配合
vue-router
,vuex
如何使用。
上篇遗留问题
动态引入client.bundle.js,webpack.server.js修改
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.ssr.html'),
filename: 'server.html',
excludeChunks: ['server'],
minify: false,
client: '/client.bundle.js'
})
]
修改后就可以去掉index.ssr.html中引入的
/client.bundle.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--vue-ssr-outlet-->
<!-- ejs模板 -->
<script src="<%=htmlWebpackPlugin.options.client%>"></script>
</body>
</html>
vue-router使用
引入vue-router
npm install vue-router
router.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Bar from './components/Bar.vue';
import Foo from './components/Foo.vue';
//使用插件
Vue.use(VueRouter);
//和前面一样原理导出一个函数
export default () => {
//路由可能是懒加载情况
const router = new VueRouter({
mode: 'history',
routers: [{
path: '/',
component: Foo
},
{
path: '/bar',
components: Bar
}
]
});
return router
}
修改main.js
import Vue from 'vue';
import App from './App.vue';
import createRouter from './router.js'
export default () => {
const router = createRouter();
const app = new Vue({
router, //注入路由系统
render: h => h(App)
})
return {
app,
router
}
}
修改App.vue
<template>
<div id='app'>
<router-link to="/">Foo</router-link>
<router-link to="/bar">Bar</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
};
</script>
修改 server-entry.js
import createApp from './app.js';
export default (context) => {
const {
url
} = context;
return new Promise((resolve, reject) => {
const {
app,
router
} = createApp();
//路径跳转
router.push(url);
//等待路由跳转完毕,组件已经准备好了触发
router.onReady(() => {
//路由匹配到的所有组件
const matchComponents = router.getMatchedComponents();
//没有可以匹配的前端路由
if (matchComponents.length === 0) {
return reject({
code: 404
});
} else {
resolve(app);
}
})
})
}
server.js修改
修改匹配方法,使用匹配所有路径
route.get('/(.*)', async (ctx) => {
ctx.body = await new Promise((resolve, reject) => {
//第一个参数对象url,是传递给server-entry.js里的context
render.renderToString({
url: ctx.url
}, (err, html) => {
if (err.code === 404) resolve('Not Found');
resolve(html);
})
})
})
vuex使用
vuex在ssr中的使用,需要注意的是,
只能在路由页面中使用
引入vuex
npm install vuex
store.js
import Vue from 'vue';
import Vuex from 'vuex';
//引入插件
Vue.use(Vuex);
export default () => {
const store = Vuex.Store({
state: {
count: 0
},
mutations: { //同步方法
increment(state, payload) {
state.count += payload;
}
},
actions: { //异步方法
asyncIncrement({
commit
}) {
return new Promise((resolve) => {
setTimeout(() => {
commit('increment', 2);
resolve();
}, 2000)
})
}
}
})
return store;
}
修改main.js
import Vue from 'vue';
import App from './App.vue';
import createRouter from './router.js';
import createStore from './store.js';
export default () => {
const router = createRouter();
const store = createStore();
const app = new Vue({
router,
store,
render: h => h(App)
})
return {
app,
store,
router
}
}
修改Foo.vue
<template>
<div>
<div>{{ $store.state.count }}</div>
</div>
</template>
<script>
export default {
//服务器端执行的方法
asyncData(store) {
console.log("111");
return store.dispatch("asyncIncrement");
},
methods: {
show() {
alert(1);
},
},
};
</script>
修改server-entry.js
import createApp from './main.js';
export default (context) => {
//当前请求路径
const {
url
} = context;
//可能存在动态路由的情况,所以这里用异步
return new Promise((resolve, reject) => {
const {
app,
router,
store
} = createApp();
//路由跳转
router.push(url);
router.onReady(() => {
//获取当前路径对应的所有组件
const matchComponents = router.getMatchComponents();
//路径下没有匹配的组件
if (matchComponents.length === 0) {
reject({
code: 404
})
} else {
//有可以匹配的组件
Promise.all(matchComponents.map(component => {
//判断组件下是有需要执行的asyncData
if (component.asyncData) {
return component.asyncData(store)
}
})).then(() => {
//服务器执行完毕后,最新的状态保存在store.state上,
//会默认在window下生成一个变量,内部默认就这样的,"window.__INITIAL_STATE__
context.state = store.state;
resolve(app);
})
}
})
})
}