├── .eslintignore ├── .stylelintignore ├── .gitignore ├── .eslintrc.js ├── examples ├── ssr-about │ ├── src │ │ ├── shims-vue.d.ts │ │ ├── views │ │ │ ├── about-us.vue │ │ │ └── about-help.vue │ │ ├── entry-client.ts │ │ ├── entry-server.ts │ │ ├── router.ts │ │ └── app.vue │ ├── genesis.build.ts │ ├── genesis.prod.ts │ ├── genesis.dev.ts │ └── genesis.ts ├── ssr-common │ ├── src │ │ ├── shims-vue.d.ts │ │ ├── entry-client.ts │ │ ├── entry-server.ts │ │ ├── router.ts │ │ ├── app.vue │ │ └── container.vue │ ├── genesis.build.ts │ ├── genesis.prod.ts │ ├── genesis.dev.ts │ └── genesis.ts └── ssr-home │ ├── src │ ├── shims-vue.d.ts │ ├── views │ │ └── home.vue │ ├── entry-client.ts │ ├── entry-server.ts │ ├── router.ts │ └── app.vue │ ├── genesis.build.ts │ ├── genesis.prod.ts │ ├── genesis.dev.ts │ └── genesis.ts ├── genesis.dev.ts ├── genesis.build.ts ├── genesis.prod.ts ├── stylelint.config.js ├── .vscode └── settings.json ├── .editorconfig ├── tsconfig.json ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/** 2 | node_modules/** 3 | examples/**/dist/** -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.js 3 | *.ts 4 | *.png 5 | *.eot 6 | *.ttf 7 | *.woff -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | **.log 3 | dist/** 4 | .idea/** 5 | examples/**/dist/** 6 | yarn.lock -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | require.resolve('@fmfe/genesis-lint') 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/ssr-about/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /examples/ssr-common/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /examples/ssr-home/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /genesis.dev.ts: -------------------------------------------------------------------------------- 1 | import './examples/ssr-common/genesis.dev'; 2 | import './examples/ssr-home/genesis.dev'; 3 | import './examples/ssr-about/genesis.dev'; 4 | -------------------------------------------------------------------------------- /genesis.build.ts: -------------------------------------------------------------------------------- 1 | import './examples/ssr-common/genesis.build'; 2 | import './examples/ssr-home/genesis.build'; 3 | import './examples/ssr-about/genesis.build'; 4 | -------------------------------------------------------------------------------- /genesis.prod.ts: -------------------------------------------------------------------------------- 1 | import './examples/ssr-common/genesis.prod'; 2 | import './examples/ssr-home/genesis.prod'; 3 | import './examples/ssr-about/genesis.prod'; 4 | -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [require.resolve('@fmfe/genesis-lint/stylelint.config')], 3 | rules: { 4 | // 添加你的自定义规则 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /examples/ssr-about/src/views/about-us.vue: -------------------------------------------------------------------------------- 1 | 2 | 关于我们 3 | 4 | 10 | -------------------------------------------------------------------------------- /examples/ssr-about/src/views/about-help.vue: -------------------------------------------------------------------------------- 1 | 2 | 帮助中心 3 | 4 | 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用项目配置规则 3 | "eslint.options": { 4 | "configFile": "./.eslintrc.js" 5 | }, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": true, 8 | "source.fixAll.stylelint": true 9 | } 10 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | #缩进风格:空格 6 | indent_style = space 7 | #缩进大小 8 | indent_size = 4 9 | #换行符lf 10 | end_of_line = lf 11 | #字符集utf-8 12 | charset = utf-8 13 | #是否删除行尾的空格 14 | trim_trailing_whitespace = true 15 | #是否在文件的最后插入一个空行 16 | insert_final_newline = true 17 | -------------------------------------------------------------------------------- /examples/ssr-home/src/views/home.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 我是首页 4 | 首页最新内容 5 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /examples/ssr-about/genesis.build.ts: -------------------------------------------------------------------------------- 1 | import { Build } from '@fmfe/genesis-compiler'; 2 | import { ssr } from './genesis'; 3 | 4 | const start = () => { 5 | /** 6 | * 创建一个编译实例 7 | */ 8 | const build = new Build(ssr); 9 | /** 10 | * 开始执行编译程序,构建生产环境应用包 11 | */ 12 | return build.start(); 13 | }; 14 | start(); 15 | -------------------------------------------------------------------------------- /examples/ssr-common/genesis.build.ts: -------------------------------------------------------------------------------- 1 | import { Build } from '@fmfe/genesis-compiler'; 2 | import { ssr } from './genesis'; 3 | 4 | const start = () => { 5 | /** 6 | * 创建一个编译实例 7 | */ 8 | const build = new Build(ssr); 9 | /** 10 | * 开始执行编译程序,构建生产环境应用包 11 | */ 12 | return build.start(); 13 | }; 14 | start(); 15 | -------------------------------------------------------------------------------- /examples/ssr-home/genesis.build.ts: -------------------------------------------------------------------------------- 1 | import { Build } from '@fmfe/genesis-compiler'; 2 | import { ssr } from './genesis'; 3 | 4 | const start = () => { 5 | /** 6 | * 创建一个编译实例 7 | */ 8 | const build = new Build(ssr); 9 | /** 10 | * 开始执行编译程序,构建生产环境应用包 11 | */ 12 | return build.start(); 13 | }; 14 | start(); 15 | -------------------------------------------------------------------------------- /examples/ssr-common/src/entry-client.ts: -------------------------------------------------------------------------------- 1 | import { ClientOptions } from '@fmfe/genesis-core'; 2 | import { createClientApp } from '@fmfe/genesis-app'; 3 | import { createRouter } from './router'; 4 | import Vue from 'vue'; 5 | import App from './app.vue'; 6 | 7 | export default async (clientOptions: ClientOptions): Promise => { 8 | return createClientApp({ 9 | App, 10 | clientOptions, 11 | vueOptions: { 12 | router: createRouter() 13 | } 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /examples/ssr-common/src/entry-server.ts: -------------------------------------------------------------------------------- 1 | import { RenderContext } from '@fmfe/genesis-core'; 2 | import { createServerApp } from '@fmfe/genesis-app'; 3 | import { createRouter } from './router'; 4 | import Vue from 'vue'; 5 | import App from './app.vue'; 6 | 7 | export default async (renderContext: RenderContext): Promise => { 8 | return createServerApp({ 9 | App, 10 | renderContext, 11 | vueOptions: { 12 | router: createRouter() 13 | } 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /examples/ssr-about/src/entry-client.ts: -------------------------------------------------------------------------------- 1 | import { ClientOptions } from '@fmfe/genesis-core'; 2 | import { createClientApp } from '@fmfe/genesis-app'; 3 | import { createRouter } from './router'; 4 | import Vue from 'vue'; 5 | import App from './app.vue'; 6 | 7 | export default async (clientOptions: ClientOptions): Promise => { 8 | return createClientApp({ 9 | App, 10 | clientOptions, 11 | vueOptions: { 12 | router: createRouter(clientOptions.state) 13 | } 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /examples/ssr-home/src/entry-client.ts: -------------------------------------------------------------------------------- 1 | import { ClientOptions } from '@fmfe/genesis-core'; 2 | import { createClientApp } from '@fmfe/genesis-app'; 3 | import { createRouter } from './router'; 4 | import Vue from 'vue'; 5 | import App from './app.vue'; 6 | 7 | export default async (clientOptions: ClientOptions): Promise => { 8 | return createClientApp({ 9 | App, 10 | clientOptions, 11 | vueOptions: { 12 | router: createRouter(clientOptions.state) 13 | } 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /examples/ssr-about/src/entry-server.ts: -------------------------------------------------------------------------------- 1 | import { RenderContext } from '@fmfe/genesis-core'; 2 | import { createServerApp } from '@fmfe/genesis-app'; 3 | import { createRouter } from './router'; 4 | import Vue from 'vue'; 5 | import App from './app.vue'; 6 | 7 | export default async (renderContext: RenderContext): Promise => { 8 | return createServerApp({ 9 | App, 10 | renderContext, 11 | vueOptions: { 12 | router: createRouter(renderContext.data.state) 13 | } 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /examples/ssr-home/src/entry-server.ts: -------------------------------------------------------------------------------- 1 | import { RenderContext } from '@fmfe/genesis-core'; 2 | import { createServerApp } from '@fmfe/genesis-app'; 3 | import { createRouter } from './router'; 4 | import Vue from 'vue'; 5 | import App from './app.vue'; 6 | 7 | export default async (renderContext: RenderContext): Promise => { 8 | return createServerApp({ 9 | App, 10 | renderContext, 11 | vueOptions: { 12 | router: createRouter(renderContext.data.state) 13 | } 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /examples/ssr-about/genesis.prod.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { ssr, app, startApp } from './genesis'; 3 | 4 | /** 5 | * 生产环境,应用程序我们已经编译好了,所以在这里可以直接创建一个渲染器 6 | */ 7 | const renderer = ssr.createRenderer(); 8 | 9 | /** 10 | * 生产环境,静态资源都是基于内容哈希生成的文件名,所以这里设置静态目录的时候,设置强缓存即可 11 | */ 12 | app.use( 13 | renderer.staticPublicPath, 14 | express.static(renderer.staticDir, { 15 | immutable: true, 16 | maxAge: '31536000000' 17 | }) 18 | ); 19 | 20 | /** 21 | * 启动应用 22 | */ 23 | startApp(renderer); 24 | -------------------------------------------------------------------------------- /examples/ssr-common/genesis.prod.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { ssr, app, startApp } from './genesis'; 3 | 4 | /** 5 | * 生产环境,应用程序我们已经编译好了,所以在这里可以直接创建一个渲染器 6 | */ 7 | const renderer = ssr.createRenderer(); 8 | 9 | /** 10 | * 生产环境,静态资源都是基于内容哈希生成的文件名,所以这里设置静态目录的时候,设置强缓存即可 11 | */ 12 | app.use( 13 | renderer.staticPublicPath, 14 | express.static(renderer.staticDir, { 15 | immutable: true, 16 | maxAge: '31536000000' 17 | }) 18 | ); 19 | 20 | /** 21 | * 启动应用 22 | */ 23 | startApp(renderer); 24 | -------------------------------------------------------------------------------- /examples/ssr-home/genesis.prod.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { ssr, app, startApp } from './genesis'; 3 | 4 | /** 5 | * 生产环境,应用程序我们已经编译好了,所以在这里可以直接创建一个渲染器 6 | */ 7 | const renderer = ssr.createRenderer(); 8 | 9 | /** 10 | * 生产环境,静态资源都是基于内容哈希生成的文件名,所以这里设置静态目录的时候,设置强缓存即可 11 | */ 12 | app.use( 13 | renderer.staticPublicPath, 14 | express.static(renderer.staticDir, { 15 | immutable: true, 16 | maxAge: '31536000000' 17 | }) 18 | ); 19 | 20 | /** 21 | * 启动应用 22 | */ 23 | startApp(renderer); 24 | -------------------------------------------------------------------------------- /examples/ssr-home/src/router.ts: -------------------------------------------------------------------------------- 1 | import { RouterMode } from 'vue-router'; 2 | import { Router } from '@fmfe/genesis-app'; 3 | 4 | interface State { 5 | routerMode?: RouterMode; 6 | } 7 | 8 | export const createRouter = (state: State) => { 9 | return new Router({ 10 | mode: state?.routerMode || 'history', 11 | routes: [ 12 | { 13 | path: '/', 14 | component: () => 15 | import( 16 | /* webpackChunkName: "home" */ './views/home.vue' 17 | ).then((m) => m.default) 18 | } 19 | ] 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /examples/ssr-about/genesis.dev.ts: -------------------------------------------------------------------------------- 1 | import { Watch } from '@fmfe/genesis-compiler'; 2 | import { ssr, app, startApp } from './genesis'; 3 | 4 | const start = async () => { 5 | /** 6 | * 创建一个观察实例 7 | */ 8 | const watch = new Watch(ssr); 9 | /** 10 | * 启动观察 11 | */ 12 | await watch.start(); 13 | /** 14 | * 拿到观察实例上对应的渲染器 15 | */ 16 | const renderer = watch.renderer; 17 | /** 18 | * 静态资源中间件 19 | */ 20 | app.use(watch.devMiddleware); 21 | /** 22 | * 热更新的中间件 23 | */ 24 | app.use(watch.hotMiddleware); 25 | /** 26 | * 拿到渲染器后,启动应用程序 27 | */ 28 | startApp(renderer); 29 | }; 30 | start(); 31 | -------------------------------------------------------------------------------- /examples/ssr-common/genesis.dev.ts: -------------------------------------------------------------------------------- 1 | import { Watch } from '@fmfe/genesis-compiler'; 2 | import { ssr, app, startApp } from './genesis'; 3 | 4 | const start = async () => { 5 | /** 6 | * 创建一个观察实例 7 | */ 8 | const watch = new Watch(ssr); 9 | /** 10 | * 启动观察 11 | */ 12 | await watch.start(); 13 | /** 14 | * 拿到观察实例上对应的渲染器 15 | */ 16 | const renderer = watch.renderer; 17 | /** 18 | * 静态资源中间件 19 | */ 20 | app.use(watch.devMiddleware); 21 | /** 22 | * 热更新的中间件 23 | */ 24 | app.use(watch.hotMiddleware); 25 | /** 26 | * 拿到渲染器后,启动应用程序 27 | */ 28 | startApp(renderer); 29 | }; 30 | start(); 31 | -------------------------------------------------------------------------------- /examples/ssr-home/genesis.dev.ts: -------------------------------------------------------------------------------- 1 | import { Watch } from '@fmfe/genesis-compiler'; 2 | import { ssr, app, startApp } from './genesis'; 3 | 4 | const start = async () => { 5 | /** 6 | * 创建一个观察实例 7 | */ 8 | const watch = new Watch(ssr); 9 | /** 10 | * 启动观察 11 | */ 12 | await watch.start(); 13 | /** 14 | * 拿到观察实例上对应的渲染器 15 | */ 16 | const renderer = watch.renderer; 17 | /** 18 | * 静态资源中间件 19 | */ 20 | app.use(watch.devMiddleware); 21 | /** 22 | * 热更新的中间件 23 | */ 24 | app.use(watch.hotMiddleware); 25 | /** 26 | * 拿到渲染器后,启动应用程序 27 | */ 28 | startApp(renderer); 29 | }; 30 | start(); 31 | -------------------------------------------------------------------------------- /examples/ssr-common/src/router.ts: -------------------------------------------------------------------------------- 1 | import { Router } from '@fmfe/genesis-app'; 2 | import Container from './container.vue'; 3 | 4 | export const createRouter = () => { 5 | return new Router({ 6 | mode: 'history', 7 | routes: [ 8 | { 9 | path: '/', 10 | meta: { 11 | ssrname: 'ssr-home' 12 | }, 13 | component: Container 14 | }, 15 | { 16 | path: '/about/*', 17 | meta: { 18 | ssrname: 'ssr-about' 19 | }, 20 | component: Container 21 | } 22 | ] 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /examples/ssr-about/src/router.ts: -------------------------------------------------------------------------------- 1 | import { RouterMode } from 'vue-router'; 2 | import { Router } from '@fmfe/genesis-app'; 3 | 4 | interface State { 5 | routerMode?: RouterMode; 6 | } 7 | 8 | export const createRouter = (state: State) => { 9 | return new Router({ 10 | mode: state?.routerMode || 'history', 11 | routes: [ 12 | { 13 | path: '/about/us', 14 | component: () => 15 | import( 16 | /* webpackChunkName: "about-us" */ './views/about-us.vue' 17 | ).then((m) => m.default) 18 | }, 19 | { 20 | path: '/about/help', 21 | component: () => 22 | import( 23 | /* webpackChunkName: "about-help" */ './views/about-help.vue' 24 | ).then((m) => m.default) 25 | } 26 | ] 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /examples/ssr-home/src/app.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ $route.fullPath }} 4 | 5 | 6 | 首页 7 | 最新 8 | 9 | 10 | 11 | 12 | 13 | 34 | 46 | -------------------------------------------------------------------------------- /examples/ssr-about/src/app.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ $route.fullPath }} 4 | 5 | 6 | 关于我们 7 | 帮助 8 | 9 | 10 | 11 | 12 | 13 | 34 | 51 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "experimentalDecorators": true, 8 | "allowJs": true, 9 | "sourceMap": true, 10 | "strict": true, 11 | "noEmit": true, 12 | "noUnusedLocals": true, 13 | "skipLibCheck": true, 14 | "noImplicitAny": false, 15 | "resolveJsonModule": true, 16 | "baseUrl": "./", 17 | "typeRoots": [ 18 | "./types/*" 19 | ], 20 | "types": [ 21 | "@types/node" 22 | ], 23 | "allowSyntheticDefaultImports": true 24 | }, 25 | "ts-node": { 26 | "compilerOptions": { 27 | "target": "es2018", 28 | "module": "commonjs", 29 | "moduleResolution": "node", 30 | "allowSyntheticDefaultImports": true, 31 | "declaration": true, 32 | "esModuleInterop": true, 33 | "outDir": "../dist" 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-genesis-micro", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "repository": "git@github.com:fmfe/vue-genesis-micro.git", 6 | "author": "followme", 7 | "license": "MIT", 8 | "scripts": { 9 | "dev": "ts-node genesis.dev -p=tsconfig.json", 10 | "build": "rm -rf dist && NODE_ENV=production ts-node genesis.build -p=tsconfig.json", 11 | "start": "NODE_ENV=production ts-node genesis.prod -p=tsconfig.json", 12 | "lint": "npm run lint:js && npm run lint:css", 13 | "lint:js": "fm-eslint . --ext .js,.ts,.vue --fix", 14 | "lint:css": "fm-stylelint . --syntax less --fix --ignore-path ./.stylelintignore | fm-stylelint . --custom-syntax postcss-html --fix" 15 | }, 16 | "dependencies": { 17 | "@fmfe/genesis-core": "1.0.3", 18 | "@types/express": "^4.17.6", 19 | "@types/node": "^14.0.1", 20 | "axios": "^0.19.2", 21 | "express": "^4.17.1", 22 | "http-proxy": "^1.18.0", 23 | "ts-node": "^8.10.2", 24 | "vue-router": "^3.5.2" 25 | }, 26 | "devDependencies": { 27 | "@fmfe/genesis-app": "1.0.3", 28 | "@fmfe/genesis-compiler": "1.0.3", 29 | "@fmfe/genesis-lint": "1.0.3", 30 | "@fmfe/genesis-remote": "1.0.3" 31 | }, 32 | "husky": { 33 | "hooks": { 34 | "pre-commit": "lint-staged" 35 | } 36 | }, 37 | "lint-staged": { 38 | "*.{ts,js}": [ 39 | "fm-eslint --ext .js,.ts --fix", 40 | "git add" 41 | ], 42 | "*.{css,less}": [ 43 | "fm-stylelint --syntax less --fix", 44 | "git add" 45 | ], 46 | "*.{vue}": [ 47 | "fm-eslint --ext .js,.ts --fix", 48 | "fm-stylelint --custom-syntax postcss-html --fix", 49 | "git add" 50 | ] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/ssr-home/genesis.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import path from 'path'; 3 | import { SSR, Renderer } from '@fmfe/genesis-core'; 4 | 5 | /** 6 | * 创建一个应用程序 7 | */ 8 | export const app = express(); 9 | 10 | /** 11 | * 创建一个 SSR 实例 12 | */ 13 | export const ssr = new SSR({ 14 | /** 15 | * 设置应用名称,不能重复 16 | */ 17 | name: 'ssr-home', 18 | /** 19 | * 设置编译的配置 20 | */ 21 | build: { 22 | /** 23 | * 设置项目的目录 24 | */ 25 | baseDir: path.resolve(__dirname) 26 | } 27 | }); 28 | 29 | /** 30 | * 拿到渲染器后,启动应用程序 31 | */ 32 | export const startApp = (renderer: Renderer) => { 33 | const renderModes = ['ssr-html', 'ssr-json', 'csr-html', 'csr-json']; 34 | /** 35 | * 提供一个API允许外部渲染 36 | */ 37 | app.use('/api/render', (req, res, next) => { 38 | // 获取渲染的地址 39 | const url = decodeURIComponent(String(req.query.renderUrl)); 40 | // 获取路由渲染的模式 41 | const routerMode = 42 | ['abstract', 'history'].indexOf(String(req.query.routerMode)) > -1 43 | ? req.query.routerMode 44 | : 'history'; 45 | // 渲染默认 46 | const mode: any = 47 | renderModes.indexOf(String(req.query.renderMode)) > -1 48 | ? String(req.query.renderMode) 49 | : 'ssr-json'; 50 | 51 | renderer 52 | .render({ 53 | url, 54 | mode, 55 | state: { 56 | routerMode 57 | } 58 | }) 59 | .then((r) => { 60 | res.send(r.data); 61 | }) 62 | .catch(next); 63 | }); 64 | /** 65 | * 使用默认渲染中间件进行渲染 66 | */ 67 | app.use(renderer.renderMiddleware); 68 | /** 69 | * 监听端口 70 | */ 71 | app.listen(3002, () => console.log(`http://localhost:3000`)); 72 | }; 73 | -------------------------------------------------------------------------------- /examples/ssr-about/genesis.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import path from 'path'; 3 | import { SSR, Renderer } from '@fmfe/genesis-core'; 4 | 5 | /** 6 | * 创建一个应用程序 7 | */ 8 | export const app = express(); 9 | 10 | /** 11 | * 创建一个 SSR 实例 12 | */ 13 | export const ssr = new SSR({ 14 | /** 15 | * 设置应用名称,不能重复 16 | */ 17 | name: 'ssr-about', 18 | /** 19 | * 设置编译的配置 20 | */ 21 | build: { 22 | /** 23 | * 设置项目的目录 24 | */ 25 | baseDir: path.resolve(__dirname) 26 | } 27 | }); 28 | 29 | /** 30 | * 拿到渲染器后,启动应用程序 31 | */ 32 | export const startApp = (renderer: Renderer) => { 33 | const renderModes = ['ssr-html', 'ssr-json', 'csr-html', 'csr-json']; 34 | /** 35 | * 提供一个API允许外部渲染 36 | */ 37 | app.use('/api/render', (req, res, next) => { 38 | // 获取渲染的地址 39 | const url = decodeURIComponent(String(req.query.renderUrl)); 40 | // 获取路由渲染的模式 41 | const routerMode = 42 | ['abstract', 'history'].indexOf(String(req.query.routerMode)) > -1 43 | ? req.query.routerMode 44 | : 'history'; 45 | // 渲染默认 46 | const mode: any = 47 | renderModes.indexOf(String(req.query.renderMode)) > -1 48 | ? String(req.query.renderMode) 49 | : 'ssr-json'; 50 | 51 | renderer 52 | .render({ 53 | url, 54 | mode, 55 | state: { 56 | routerMode 57 | } 58 | }) 59 | .then((r) => { 60 | res.send(r.data); 61 | }) 62 | .catch(next); 63 | }); 64 | /** 65 | * 使用默认渲染中间件进行渲染 66 | */ 67 | app.use(renderer.renderMiddleware); 68 | /** 69 | * 监听端口 70 | */ 71 | app.listen(3001, () => console.log(`http://localhost:3000`)); 72 | }; 73 | -------------------------------------------------------------------------------- /examples/ssr-common/src/app.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ $route.fullPath }} 5 | Back 6 | Borward 7 | {{ isLogin ? '已登录' : '未登录' }} 8 | 切换登录状态 9 | 10 | 11 | ssr-common 12 | 13 | 14 | 首页 15 | 16 | 17 | 关于我们 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 48 | 60 | 96 | -------------------------------------------------------------------------------- /examples/ssr-common/src/container.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 78 | -------------------------------------------------------------------------------- /examples/ssr-common/genesis.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import path from 'path'; 3 | import httpProxy from 'http-proxy'; 4 | import { SSR, Renderer } from '@fmfe/genesis-core'; 5 | 6 | /** 7 | * 创建一个应用程序 8 | */ 9 | export const app = express(); 10 | 11 | /** 12 | * 创建一个 SSR 实例 13 | */ 14 | export const ssr = new SSR({ 15 | /** 16 | * 设置应用名称,不能重复 17 | */ 18 | name: 'ssr-common', 19 | /** 20 | * 设置编译的配置 21 | */ 22 | build: { 23 | /** 24 | * 设置项目的目录 25 | */ 26 | baseDir: path.resolve(__dirname) 27 | } 28 | }); 29 | 30 | /** 31 | * 拿到渲染器后,启动应用程序 32 | */ 33 | export const startApp = (renderer: Renderer) => { 34 | const proxy = httpProxy.createProxyServer({}); 35 | /** 36 | * 注意:这里是为了演示,所以在聚合服务上代理了其它服务的请求,在生产环境中,你可能会使用 Nginx 进行反向代理。 37 | */ 38 | /** 39 | * ssr-home 服务静态文件代理 40 | */ 41 | app.use('/ssr-home/', (req, res, next) => { 42 | proxy.web( 43 | req, 44 | res, 45 | { 46 | target: 'http://localhost:3002/ssr-home/', 47 | changeOrigin: true 48 | }, 49 | (err) => { 50 | res.status(500).send(err.message); 51 | } 52 | ); 53 | }); 54 | /** 55 | * ssr-home api 代理 56 | */ 57 | app.use('/api/ssr-home/', (req, res, next) => { 58 | proxy.web( 59 | req, 60 | res, 61 | { 62 | target: 'http://localhost:3002/api/', 63 | changeOrigin: true 64 | }, 65 | (err) => { 66 | res.status(500).send(err.message); 67 | } 68 | ); 69 | }); 70 | /** 71 | * ssr-about 静态资源代理 72 | */ 73 | app.use('/ssr-about/', (req, res, next) => { 74 | proxy.web( 75 | req, 76 | res, 77 | { 78 | target: 'http://localhost:3001/ssr-about', 79 | changeOrigin: true 80 | }, 81 | (err) => { 82 | res.status(500).send(err.message); 83 | } 84 | ); 85 | }); 86 | /** 87 | * ssr-about api 代理 88 | */ 89 | app.use('/api/ssr-about', (req, res, next) => { 90 | proxy.web( 91 | req, 92 | res, 93 | { 94 | target: 'http://localhost:3001/api/', 95 | changeOrigin: true 96 | }, 97 | (err) => { 98 | res.status(500).send(err.message); 99 | } 100 | ); 101 | }); 102 | /** 103 | * 使用默认渲染中间件进行渲染,你也可以调用更加底层的 renderer.renderJson 和 renderer.renderHtml 来实现渲染 104 | */ 105 | app.use(renderer.renderMiddleware); 106 | /** 107 | * 监听端口 108 | */ 109 | app.listen(3000, () => console.log(`http://localhost:3000`)); 110 | }; 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 快速开始 2 | ```bash 3 | # 拉取代码 4 | git clone git@github.com:fmfe/vue-genesis-micro.git 5 | # 进入项目目录 6 | cd vue-genesis-micro 7 | # 安装依赖 8 | npm install 9 | # 开发环境启动 10 | npm run dev 11 | # 打包生产环境代码 12 | npm run build 13 | # 生产环境运行 14 | npm run start 15 | ``` 16 | ## 微服务是什么? 17 | 微服务是一个新兴的软件架构,就是把一个大型的单个应用程序和服务拆分为数十个的支持微服务。 18 | 19 | ## 为什么需要微服务? 20 | 随着业务的发展,项目规模越来越大,给编译打包、合并的代码冲突带来了巨大的挑战,而服务的拆分可以获得更快的编译打包,独立部署、增量更新、不同的团队只需要负责自己的服务、更好的支持多端,在大型的项目中,使用微服务架构可以获得极大的收益。 21 | 22 | ## 微服务和微前端的区别 23 | 目前社区的微前端解决方案,基本上都是基于客户端去进行聚合的思路,而本项目却是完全基于后端微服务的概念而诞生的,页面的聚合既可以在服务端完成,也可以在客户端完成,一切取决于需求。 24 | 25 | ## 项目介绍 26 | 本项目基于 Vue 的[Genesis](https://github.com/fmfe/genesis)开发,一个编写了三个例子: 27 | - `ssr-common` 公共的页面导航 28 | - `ssr-home` 首页 29 | - `ssr-about` 关于我们 30 | 在学习完成本项目后,你可以搭建属于自己的微服务架构,并且深入的了解到[远程组件](https://fmfe.github.io/genesis-docs/remote/)它是怎么工作的。至此,你可以做到一个大型应用的微服务拆分下。 31 | 32 | ## 关于 Genesis 33 | [Genesis](https://github.com/fmfe/genesis)是在[FOLLOWME5.0](https://www.followme.com/)升级而诞生的一个项目,它解决了以往架构的很多弊端,例如: 34 | - 公共组件库更新,导致同时十几个项目要编译发布更新 35 | - 页面和页面之间的切换,需要刷新整页,无法做到无刷新跳转 36 | - 数百个页面,如果全部写到一个大的项目中做SSR渲染,只要其中一个地方出现BUG,就有可能导致整个服务挂掉或者不稳定,发生内存泄漏的问题时,也更加难以排查 37 | - 大量的项目是基于CSR渲染,在国际化和SEO方面,导致 index.html 页面的标题、关键词和描述比较难做到国际化 38 | - 不同的团队,互相交叉开发一个项目,合并代码时,很容易产生各种冲突,服务的拆分后,大大的减少了代码冲突 39 | 40 | ## 渲染接口 41 | 服务的拆分后,那么服务和服务之间的调用是必不可少的,这里提出了一个渲染接口的概念,它可能是这样子的 42 | ```ts 43 | // 下面的接口,你可能需要做Nginx反向代理,来做到下面的接口 44 | // /api/ssr-服务名称/render?url=渲染地址&mode=渲染模式&routerMode=路由模式 45 | const renderModes = ['ssr-html', 'ssr-json', 'csr-html', 'csr-json']; 46 | /** 47 | * 提供一个API允许外部渲染 48 | */ 49 | app.use('/api/render', (req, res, next) => { 50 | // 获取渲染的地址 51 | const url = decodeURIComponent(String(req.query.renderUrl)); 52 | // 获取路由渲染的模式 53 | const routerMode = 54 | ['abstract', 'history'].indexOf(String(req.query.routerMode)) > -1 55 | ? req.query.routerMode 56 | : 'history'; 57 | // 渲染默认 58 | const mode: any = 59 | renderModes.indexOf(String(req.query.renderMode)) > -1 60 | ? String(req.query.renderMode) 61 | : 'ssr-json'; 62 | 63 | renderer 64 | .render({ 65 | url, 66 | mode, 67 | state: { 68 | routerMode 69 | } 70 | }) 71 | .then((r) => { 72 | res.send(r.data); 73 | }) 74 | .catch(next); 75 | }); 76 | ``` 77 | 这样第三方的服务,就可以随意的调用这个服务的渲染结果,传递需要渲染的地方渲染,比如Vue、React、或者其它的EJS模板引擎等等。本项目会使用渲染接口,来传递给远程组件进行渲染。 78 | 79 | ## 远程组件 80 | 当你需要调用其它服务的页面渲染时,你请求渲染接口,拿到渲染的结果,传递给远程组件,它就会负责帮你渲染该服务的内容。 81 | ```vue 82 | 83 | 84 | 90 | 91 | 92 | 159 | 160 | ``` 161 | 162 | ## 目录说明 163 | ``` 164 | . 165 | ├── .vscode 166 | │ ├── settings.json vscode的配置 167 | ├── examples 服务拆分的例子 168 | │ ├── ssr-about 关于我们服务 169 | │ | ├── src Vue源码目录 170 | │ | | ├── views 页面目录 171 | │ | | | ├── about-help.vue 帮助中心页面 172 | │ | | | └── about-us.vue 关于我们页面 173 | │ | | ├── app.vue 页面入口文件,公共导航 174 | │ | | ├── entry-client.ts 客户端入口文件 175 | │ | | ├── entry-server.ts 服务端入口文件 176 | │ | | ├── router.ts 路由配置文件 177 | │ | | └── shims-vue.d.ts .vue文件的TS声明 178 | │ | ├── genesis.build.ts 当前服务生产环境构建入口 179 | │ | ├── genesis.dev.ts 当前服务开发环境入口 180 | │ | ├── genesis.prod.ts 当前服务生产环境入口 181 | │ | └── genesis.ts 当前服务通用的服务端逻辑 182 | │ ├── ssr-common 基础的页面聚合服务,包含公共导航 183 | │ | ├── src Vue源码目录 184 | │ | | ├── views 页面目录 185 | │ | | | ├── about-help.vue 帮助中心页面 186 | │ | | | └── about-us.vue 关于我们页面 187 | │ | | ├── app.vue 页面入口文件,公共导航 188 | │ | | ├── container.vue 子应用的容器 189 | │ | | ├── entry-client.ts 客户端入口文件 190 | │ | | ├── entry-server.ts 服务端入口文件 191 | │ | | ├── router.ts 路由配置文件 192 | │ | | └── shims-vue.d.ts .vue文件的TS声明 193 | │ | ├── genesis.build.ts 当前服务生产环境构建入口 194 | │ | ├── genesis.dev.ts 当前服务开发环境入口 195 | │ | ├── genesis.prod.ts 当前服务生产环境入口 196 | │ | └── genesis.ts 当前服务通用的服务端逻辑 197 | │ ├── ssr-home 首页的服务 198 | │ | ├── src Vue源码目录 199 | │ | | ├── views 页面目录 200 | │ | | | └── home.vue 首页页面 201 | │ | | ├── app.vue 页面入口文件,公共导航 202 | │ | | ├── container.vue 子应用的容器 203 | │ | | ├── entry-client.ts 客户端入口文件 204 | │ | | ├── entry-server.ts 服务端入口文件 205 | │ | | ├── router.ts 路由配置文件 206 | │ | | └── shims-vue.d.ts .vue文件的TS声明 207 | │ | ├── genesis.build.ts 当前服务生产环境构建入口 208 | │ | ├── genesis.dev.ts 当前服务开发环境入口 209 | │ | ├── genesis.prod.ts 当前服务生产环境入口 210 | │ | └── genesis.ts 当前服务通用的服务端逻辑 211 | | ├── .editorconfig 编辑器配置 212 | | ├── .eslintignore eslint忽略配置 213 | | ├── .eslintrc.js eslint配置 214 | | ├── .gitignore git忽略配置 215 | | ├── .stylelintignore stylelint忽略配置 216 | | ├── genesis.build.ts 所有服务生产构建 217 | | ├── genesis.dev.ts 所有服务开发环境启动 218 | | ├── genesis.prod.ts 所有服务生产环境入口 219 | | ├── stylelint.config.js stylelint配置 220 | | └── tsconfig.json TS的配置 221 | │ 222 | └── package.json 223 | ``` 224 | 注意:本项目是为了演示,才把几个服务全部放到一个仓库中,在实际的应用中,每一个服务,都应该放到独立的git仓库中。 225 | 226 | ## 最后 227 | - 本项目源码地址:[vue-genesis-micro](https://github.com/fmfe/vue-genesis-micro) 228 | - [Genesis](https://github.com/fmfe/genesis)官方仓库 229 | - [Genesis](https://fmfe.github.io/genesis-docs/)文档地址 230 | - [基于 Vue SSR 的微架构在 FOLLOWME5.0 实践](https://fmfe.github.io/genesis-docs/blog/followme5.0.html) 231 | - [基于 Vue CSR 的微前端实现方案](https://fmfe.github.io/genesis-docs/blog/2020-05-25.html) 232 | 如果你对微前端、和微服务这一块感兴趣,欢迎 Star ,也可以在留言区我和互动! 233 | --------------------------------------------------------------------------------
我是首页
首页最新内容