vue-cli3搭建的vue改造成SSR项目
- 一、文章简介
- 二、搭建vue项目
- 三、改造成SSR
- 首先安装ssr的所需相关依赖
- 改造router.ts
- 改造store.ts
- 改造main.ts
- 创建entry-client.ts
- 创建entry-server.ts
- 创建index.template.html
- 创建server.ts
- 构建配置
- 修改package.json
- 四、启动项目
一、文章简介
本文是本人一个随记,主要是讲述的是vue+typescript+ssr+koa2项目基础搭建。
二、搭建vue项目
1、创建项目
vue create vue-ssr
由于我的工程比较大,所以需要的东西比较多,请按各自项目需求选择;
后面主要的选择node-sass、ESLint + Prettier、Lint and fix on commit、Jest;之后就自动开始下载所需依赖;
2、目录结构
3、运行项目
npm run serve
如果出现以上情况,说明项目依赖下载成功;
三、改造成SSR
首先安装ssr的所需相关依赖
安装 vue-server-renderer
安装 webpack-node-externals
安装 cross-env
安装 koa koa-static
安装 vuex-router-sync(用于连接store和router,这样就可以在组件中直接访问 this.$store.state.route)
npm install vue-server-renderer webpack-node-externals cross-env --save-dev
npm install koa koa-static vuex-router-sync --save
改造router.ts
import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: "/",
name: "home",
component: () => import("../views/Home.vue"),
},
{
path: "/about",
name: "about",
component: () => import("../views/About.vue"),
},
];
export default function createRouter() {
return new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
}
改造store.ts
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default function createStore() {
return new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {},
});
}
改造main.ts
import Vue from "vue";
import App from "./App.vue";
import createRouter from "./router";
import createStore from "./store";
import { sync } from "vuex-router-sync";
Vue.config.productionTip = false;
export default function createApp() {
const router = createRouter();
const store = createStore();
sync(store, router);
const app = new Vue({
router,
store,
render: (h) => h(App),
});
return { app, router, store };
}
创建entry-client.ts
在src根目录下创建entry-client.ts文件;
import createApp from "./main";
const { app, router, store } = createApp();
if ((window as any).__INITIAL_STATE__) {
store.replaceState((window as any).__INITIAL_STATE__);
}
router.onReady(() => {
router.beforeResolve((to, from, next) => {
const matched: any = router.getMatchedComponents(to);
const prevMatched: any = router.getMatchedComponents(from);
let diffed = false;
const activated = matched.filter((c: any, i: number) => {
return diffed || (diffed = prevMatched[i] !== c);
});
if (!activated.length) {
return next();
}
Promise.all(
activated.map((c: any) => {
if (c.asyncData) {
return c.asyncData({ store, route: to });
}
})
)
.then(() => {
next();
})
.catch(next);
});
app.$mount("#app");
});
window.INITIAL_STATE 保存的是服务端返回的 context.state,客户端在挂载之前,将其替换到 store.state 中。
创建entry-server.ts
在src根目录下创建entry-server.ts文件;
import createApp from "./main";
export default (context: any) => {
return new Promise((resolve, reject) => {
const { app, router, store } = createApp();
router.push(context.url);
router.onReady(() => {
const matchedComponents = router.getMatchedComponents();
if (!matchedComponents.length) {
return reject({ code: 404 });
}
Promise.all(
matchedComponents.map((Component: any) => {
if (Component.asyncData) {
return Component.asyncData({
store,
route: router.currentRoute,
});
}
})
)
.then(() => {
context.state = store.state;
resolve(app);
})
.catch(reject);
}, reject);
});
};
创建index.template.html
在src根目录下创建index.template.html文件;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{title}}</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
创建server.ts
由于我的项目需求写一些业务逻辑,所以我这里面创建了server文件夹,在server下创建了index.ts;
所以在创建文件以及配置的注意文件路径
const fs = require("fs");
const Koa = require("koa");
const path = require("path");
const koaStatic = require("koa-static");
const app = new Koa();
const resolve = (file) => path.resolve(__dirname, file);
app.use(koaStatic(resolve("../dist/client")));
const { createBundleRenderer } = require("vue-server-renderer");
const serverBundle = require("../dist/server/vue-ssr-server-bundle.json");
const clientManifest = require("../dist/client/vue-ssr-client-manifest.json");
const renderer = createBundleRenderer(serverBundle, {
runInNewContext: false,
template: fs.readFileSync(
path.resolve(__dirname, "../src/index.template.html"),
"utf-8"
),
clientManifest,
});
function renderToString(context) {
return new Promise((resolve, reject) => {
renderer.renderToString(context, (err, html) => {
err ? reject(err) : resolve(html);
});
});
}
app.use(async (ctx, next) => {
const context = {
title: "Hello SSR",
url: ctx.url,
};
const html = await renderToString(context);
ctx.body = html;
});
const port = 3000;
app.listen(port, function() {
console.log(`server started at localhost:${port}`);
});
构建配置
在项目根目录下创建vue.config.js
const VueSSRServerPlugin = require("vue-server-renderer/server-plugin");
const VueSSRClientPlugin = require("vue-server-renderer/client-plugin");
const nodeExternals = require("webpack-node-externals");
const env = process.env;
const isServer = env.RUN_ENV === "server";
module.exports = {
lintOnSave: false,
publicPath: "./",
outputDir: `dist/${env.RUN_ENV}`,
configureWebpack: {
entry: `./src/entry-${env.RUN_ENV}.ts`,
devtool: "eval",
target: isServer ? "node" : "web",
output: {
libraryTarget: isServer ? "commonjs2" : undefined,
},
externals: isServer
? nodeExternals({
whitelist: /\.css$/,
})
: undefined,
optimization: { splitChunks: isServer ? false : undefined },
plugins: [isServer ? new VueSSRServerPlugin() : new VueSSRClientPlugin()],
},
};
修改package.json
在文件中的script对象中添加以下命令:
"start": "npm run build:server && npm run build:client && npm run service",
"build:client": "cross-env RUN_ENV=client vue-cli-service build",
"build:server": "cross-env RUN_ENV=server vue-cli-service build --mode server",
"service": "node server\\index.ts"
添加完后就可以开始跑项目了;
四、启动项目
npm run start
执行以上命令会先build server 环境后build client环境 之后开始启动项目
注:以上步骤为初稿,后续会持续更新完善
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)