@mpxjs/vue-cli-plugin-mpx-ssr 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/commands/build/index.js +57 -0
- package/commands/serve/index.js +49 -0
- package/commands/serve/serveServer.js +56 -0
- package/config/base.config.js +19 -0
- package/config/build.config.js +8 -0
- package/config/serve.config.js +10 -0
- package/generator/index.js +15 -0
- package/generator/template/public/favicon.ico +0 -0
- package/generator/template/public/index.ssr.html +14 -0
- package/generator/template/server/build.server.js +44 -0
- package/generator/template/server/dev.server.js +63 -0
- package/generator/template/src/app.mpx +57 -0
- package/index.js +14 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { normalizeCommandArgs } = require('@mpxjs/cli-shared-utils')
|
|
2
|
+
const { addBaseWebpackConfig } = require('../../config/base.config')
|
|
3
|
+
const { addBuildWebpackConfig } = require('../../config/build.config')
|
|
4
|
+
const { resolveBuildWebpackConfigByTarget } = require('@mpxjs/vue-cli-plugin-mpx/config')
|
|
5
|
+
const { getCurrentTarget } = require('@mpxjs/cli-shared-utils')
|
|
6
|
+
const { handleWebpackDone } = require('@mpxjs/vue-cli-plugin-mpx/utils/webpack')
|
|
7
|
+
const webpack = require('webpack')
|
|
8
|
+
const fs = require('fs-extra')
|
|
9
|
+
|
|
10
|
+
const defaults = {
|
|
11
|
+
clean: true
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports.registerBuildCommand = function (api, options) {
|
|
15
|
+
api.registerCommand(
|
|
16
|
+
'build:ssr',
|
|
17
|
+
{
|
|
18
|
+
description: 'mpx production',
|
|
19
|
+
usage: 'mpx-cli-service build ssr'
|
|
20
|
+
},
|
|
21
|
+
function build (args) {
|
|
22
|
+
normalizeCommandArgs(args, defaults)
|
|
23
|
+
if (args.clean) {
|
|
24
|
+
fs.removeSync(options.outputDir)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const getBaseConfig = (ssrMode) => {
|
|
28
|
+
api.chainWebpack((config) => {
|
|
29
|
+
addBaseWebpackConfig(api, options, args, config, { ssrMode })
|
|
30
|
+
addBuildWebpackConfig(api, options, args, config, { ssrMode })
|
|
31
|
+
})
|
|
32
|
+
const target = getCurrentTarget()
|
|
33
|
+
// 根据目标获取构建配置
|
|
34
|
+
const webpackConfig = resolveBuildWebpackConfigByTarget(api, options, target, args)
|
|
35
|
+
return {
|
|
36
|
+
webpackConfig,
|
|
37
|
+
target
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const buildService = (ssrMode) => {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const { webpackConfig, target } = getBaseConfig(ssrMode)
|
|
44
|
+
webpack(webpackConfig, (err, stats) => {
|
|
45
|
+
handleWebpackDone(err, stats, target, api)
|
|
46
|
+
.then((...res) => {
|
|
47
|
+
resolve(...res)
|
|
48
|
+
})
|
|
49
|
+
.catch(reject)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return Promise.all([buildService('client'), buildService('server')])
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { normalizeCommandArgs } = require('@mpxjs/cli-shared-utils')
|
|
2
|
+
const { serveWeb } = require('@mpxjs/vue-cli-plugin-mpx/commands/serve/web')
|
|
3
|
+
const { serveServer } = require('./serveServer')
|
|
4
|
+
const { addBaseWebpackConfig } = require('../../config/base.config')
|
|
5
|
+
const { addServeWebpackConfig } = require('../../config/serve.config')
|
|
6
|
+
|
|
7
|
+
const defaults = {
|
|
8
|
+
port: 3000
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports.registerServeCommand = function (api, options) {
|
|
12
|
+
api.registerCommand(
|
|
13
|
+
'serve:ssr',
|
|
14
|
+
{
|
|
15
|
+
description: 'mpx development',
|
|
16
|
+
usage: 'mpx-cli-service serve ssr'
|
|
17
|
+
},
|
|
18
|
+
async function build (args) {
|
|
19
|
+
normalizeCommandArgs(args, defaults)
|
|
20
|
+
|
|
21
|
+
const getBaseConfig = (ssrMode) => {
|
|
22
|
+
const isServer = ssrMode === 'server'
|
|
23
|
+
const port = options.pluginOptions?.SSR?.devClientPort || defaults.port
|
|
24
|
+
options.publicPath = isServer ? '/' : `http://localhost:${port}/`
|
|
25
|
+
api.chainWebpack((config) => {
|
|
26
|
+
addBaseWebpackConfig(api, options, args, config, { ssrMode })
|
|
27
|
+
addServeWebpackConfig(api, options, args, config, { ssrMode })
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const buildService = (ssrMode) => {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
getBaseConfig(ssrMode)
|
|
34
|
+
if (ssrMode === 'client') {
|
|
35
|
+
serveWeb(api, options, args).then((...res) => {
|
|
36
|
+
resolve(...res)
|
|
37
|
+
}).catch(reject)
|
|
38
|
+
} else {
|
|
39
|
+
serveServer(api, options, args).then((...res) => {
|
|
40
|
+
resolve(...res)
|
|
41
|
+
}).catch(reject)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Promise.all([buildService('client'), buildService('server')])
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const { resolveServeWebpackConfigByTarget } = require('@mpxjs/vue-cli-plugin-mpx/config')
|
|
2
|
+
const { getCurrentTarget } = require('@mpxjs/cli-shared-utils/lib')
|
|
3
|
+
const { getReporter } = require('@mpxjs/vue-cli-plugin-mpx/utils/reporter')
|
|
4
|
+
const { chalk } = require('@vue/cli-shared-utils')
|
|
5
|
+
const webpack = require('webpack')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
const MemoryFS = require('memory-fs')
|
|
8
|
+
const { setServerBundle } = require('@mpxjs/cli-shared-utils')
|
|
9
|
+
|
|
10
|
+
/** @type {import('@vue/cli-service').ServicePlugin} */
|
|
11
|
+
module.exports.serveServer = async (api, options, args) => {
|
|
12
|
+
// resolve webpack config
|
|
13
|
+
const target = getCurrentTarget()
|
|
14
|
+
const webpackConfig = resolveServeWebpackConfigByTarget(
|
|
15
|
+
api,
|
|
16
|
+
options,
|
|
17
|
+
target,
|
|
18
|
+
args
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// create compiler
|
|
22
|
+
const serverCompiler = webpack(webpackConfig)
|
|
23
|
+
|
|
24
|
+
const mfs = new MemoryFS()
|
|
25
|
+
// 指定输出到的内存流中
|
|
26
|
+
serverCompiler.outputFileSystem = mfs
|
|
27
|
+
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
serverCompiler.watch({}, (err, stats) => {
|
|
30
|
+
if (err) {
|
|
31
|
+
console.error(err)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
stats = stats.toJson()
|
|
35
|
+
stats.errors.forEach(error => console.error(error))
|
|
36
|
+
stats.warnings.forEach(warn => console.warn(warn))
|
|
37
|
+
|
|
38
|
+
const bundlePath = path.join(webpackConfig.output.path, 'vue-ssr-server-bundle.json')
|
|
39
|
+
|
|
40
|
+
const serverManifest = JSON.parse(mfs.readFileSync(bundlePath, 'utf-8'))
|
|
41
|
+
|
|
42
|
+
setServerBundle(serverManifest)
|
|
43
|
+
|
|
44
|
+
getReporter()._renderStates([{
|
|
45
|
+
message: 'new server bundle generated',
|
|
46
|
+
color: 'green'
|
|
47
|
+
}])
|
|
48
|
+
})
|
|
49
|
+
try {
|
|
50
|
+
require(api.resolve('server/dev.server'))
|
|
51
|
+
} catch (e) {
|
|
52
|
+
reject(e)
|
|
53
|
+
}
|
|
54
|
+
resolve()
|
|
55
|
+
})
|
|
56
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
|
|
2
|
+
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
|
|
3
|
+
|
|
4
|
+
module.exports.addBaseWebpackConfig = function (api, options = {}, args, config, compilerConfig) {
|
|
5
|
+
const isServer = compilerConfig.ssrMode === 'server'
|
|
6
|
+
|
|
7
|
+
config.target(isServer ? 'node' : 'web')
|
|
8
|
+
|
|
9
|
+
config.output
|
|
10
|
+
.libraryTarget(isServer ? 'commonjs2' : undefined)
|
|
11
|
+
|
|
12
|
+
if (isServer) {
|
|
13
|
+
config.optimization.splitChunks(false)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
config
|
|
17
|
+
.plugin(`${isServer ? 'server-plugin' : 'client-plugin'}`)
|
|
18
|
+
.use(isServer ? VueSSRServerPlugin : VueSSRClientPlugin)
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module.exports.addBuildWebpackConfig = function (api, options = {}, args, config, compilerConfig) {
|
|
2
|
+
const isServer = compilerConfig.ssrMode === 'server'
|
|
3
|
+
|
|
4
|
+
config.cache({
|
|
5
|
+
...config.get('cache'),
|
|
6
|
+
name: isServer ? 'build-serverBundleCache' : 'build-clientBundleCache'
|
|
7
|
+
})
|
|
8
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module.exports.addServeWebpackConfig = function (api, options = {}, args, config, compilerConfig) {
|
|
2
|
+
const isServer = compilerConfig.ssrMode === 'server'
|
|
3
|
+
|
|
4
|
+
config.cache({
|
|
5
|
+
...config.get('cache'),
|
|
6
|
+
name: isServer ? 'dev-serverBundleCache' : 'dev-clientBundleCache'
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
config.devServer.port(options.pluginOptions?.SSR?.devClientPort || 3000)
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module.exports = function (api, options) {
|
|
2
|
+
api.render('./template', options)
|
|
3
|
+
api.extendPackage({
|
|
4
|
+
scripts: {
|
|
5
|
+
'serve:ssr': 'mpx-cli-service serve:ssr',
|
|
6
|
+
'build:ssr': 'mpx-cli-service build:ssr'
|
|
7
|
+
},
|
|
8
|
+
devDependencies: {
|
|
9
|
+
axios: '^1.6.0',
|
|
10
|
+
express: '^4.18.2',
|
|
11
|
+
'serve-favicon': '^2.5.0',
|
|
12
|
+
'vue-server-renderer': '^2.7.14'
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport"
|
|
6
|
+
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
7
|
+
<meta name="format-detection" content="telephone=no">
|
|
8
|
+
<meta name="x5-cache" content="disable">
|
|
9
|
+
<title></title>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<!--vue-ssr-outlet-->
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const express = require('express')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
|
|
4
|
+
const app = express()
|
|
5
|
+
|
|
6
|
+
// 创建渲染器 获得一个createBundleRenderer
|
|
7
|
+
const { createBundleRenderer } = require('vue-server-renderer')
|
|
8
|
+
const serverBundle = require('../dist/web/vue-ssr-server-bundle.json')
|
|
9
|
+
const clientManifest = require('../dist/web/vue-ssr-client-manifest.json')
|
|
10
|
+
const template = fs.readFileSync('../public/index.ssr.html', 'utf-8')
|
|
11
|
+
|
|
12
|
+
app.use(express.static('../dist/web'))
|
|
13
|
+
|
|
14
|
+
const startServer = async () => {
|
|
15
|
+
// 前端请求返回数据
|
|
16
|
+
app.get('*', async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const renderer = createBundleRenderer(serverBundle, {
|
|
19
|
+
runInNewContext: false,
|
|
20
|
+
template,
|
|
21
|
+
clientManifest
|
|
22
|
+
})
|
|
23
|
+
const context = { url: req.url }
|
|
24
|
+
renderer.renderToString(context, (err, html) => {
|
|
25
|
+
if (err) {
|
|
26
|
+
console.log(err)
|
|
27
|
+
res.status(500).end('Internal Server Error')
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
res.end(html)
|
|
31
|
+
})
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.log(error)
|
|
34
|
+
res.status(500).send('服务器内部错误')
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/* 服务启动 */
|
|
39
|
+
const port = 8091
|
|
40
|
+
app.listen(port, () => {
|
|
41
|
+
console.log(`server started at localhost:${port}`)
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
startServer()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const express = require('express')
|
|
2
|
+
const { getServerBundle } = require('@mpxjs/mpx-cli-service')
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
|
|
6
|
+
// 创建渲染器 获得一个createBundleRenderer
|
|
7
|
+
const { createBundleRenderer } = require('vue-server-renderer')
|
|
8
|
+
|
|
9
|
+
const axios = require('axios')
|
|
10
|
+
|
|
11
|
+
const favicon = require('serve-favicon')
|
|
12
|
+
|
|
13
|
+
const template = fs.readFileSync(path.resolve('public/index.ssr.html'), 'utf-8')
|
|
14
|
+
|
|
15
|
+
const vueConfig = require('../vue.config.js')
|
|
16
|
+
|
|
17
|
+
const app = express()
|
|
18
|
+
|
|
19
|
+
app.use(favicon(path.resolve('public/favicon.ico')))
|
|
20
|
+
|
|
21
|
+
const getRenderer = async () => {
|
|
22
|
+
const clientPort = vueConfig.pluginOptions?.SSR?.devClientPort || 3000
|
|
23
|
+
const clientManifest = await axios.get(
|
|
24
|
+
`http://localhost:${clientPort}/vue-ssr-client-manifest.json`
|
|
25
|
+
)
|
|
26
|
+
const serverManifest = getServerBundle()
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
clientManifest: clientManifest.data,
|
|
30
|
+
serverManifest: serverManifest
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const startServer = async () => {
|
|
35
|
+
app.get('*', async (req, res) => {
|
|
36
|
+
try {
|
|
37
|
+
const { clientManifest, serverManifest } = await getRenderer()
|
|
38
|
+
const renderer = createBundleRenderer(serverManifest, {
|
|
39
|
+
runInNewContext: false,
|
|
40
|
+
template,
|
|
41
|
+
clientManifest
|
|
42
|
+
})
|
|
43
|
+
const context = { url: req.url }
|
|
44
|
+
renderer.renderToString(context, (err, html) => {
|
|
45
|
+
if (err) {
|
|
46
|
+
res.status(500).end('Internal Server Error')
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
res.end(html)
|
|
50
|
+
})
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.log(error)
|
|
53
|
+
res.status(500).send('服务器内部错误')
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
/* 服务启动 */
|
|
58
|
+
const port = 8091
|
|
59
|
+
app.listen(port, () => {
|
|
60
|
+
console.log(`server started at localhost:${port}`)
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
startServer()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import mpx, { createApp } from '@mpxjs/core'
|
|
3
|
+
import apiProxy from '@mpxjs/api-proxy'
|
|
4
|
+
import { createPinia } from '@mpxjs/pinia'
|
|
5
|
+
|
|
6
|
+
mpx.use(apiProxy, { usePromise: true })
|
|
7
|
+
|
|
8
|
+
mpx.config.webRouteConfig = {
|
|
9
|
+
mode: 'history'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
createApp({
|
|
13
|
+
onLaunch() {
|
|
14
|
+
//
|
|
15
|
+
},
|
|
16
|
+
onAppInit() {
|
|
17
|
+
const pinia = createPinia()
|
|
18
|
+
return {
|
|
19
|
+
pinia
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
onSSRAppCreated({ pinia, router, app, context }) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
router.push(context.url)
|
|
25
|
+
router.onReady(() => {
|
|
26
|
+
context.rendered = () => {
|
|
27
|
+
context.state = pinia.state.value
|
|
28
|
+
}
|
|
29
|
+
resolve(app)
|
|
30
|
+
}, reject)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<style>
|
|
37
|
+
/*此处编写全局样式*/
|
|
38
|
+
</style>
|
|
39
|
+
|
|
40
|
+
<script type="application/json">
|
|
41
|
+
{
|
|
42
|
+
"pages": [
|
|
43
|
+
"./pages/index"
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<!--也可以通过以下形式用js输出json,便于书写注释和使用条件编译-->
|
|
49
|
+
|
|
50
|
+
<!--<script name="json">-->
|
|
51
|
+
<!-- // 可以写注释,通过defs注入的常量做一些判断之类的操作-->
|
|
52
|
+
<!-- module.exports = {-->
|
|
53
|
+
<!-- pages: [-->
|
|
54
|
+
<!-- './pages/index'-->
|
|
55
|
+
<!-- ]-->
|
|
56
|
+
<!-- }-->
|
|
57
|
+
<!--</script>-->
|
package/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const { registerServeCommand } = require('./commands/serve')
|
|
2
|
+
const { registerBuildCommand } = require('./commands/build')
|
|
3
|
+
|
|
4
|
+
module.exports = function (api, options) {
|
|
5
|
+
registerServeCommand(api, options)
|
|
6
|
+
registerBuildCommand(api, options)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports.defaultModes = {
|
|
10
|
+
'serve:ssr': 'development',
|
|
11
|
+
'build:ssr': 'production'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports.after = ['@mpxjs/vue-cli-plugin-mpx']
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mpxjs/vue-cli-plugin-mpx-ssr",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": " web ssr plugin for mpx-cli",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"memory-fs": "^0.5.0",
|
|
11
|
+
"fs-extra": "^9.1.0",
|
|
12
|
+
"@mpxjs/vue-cli-plugin-mpx": "^2.1.0",
|
|
13
|
+
"@mpxjs/cli-shared-utils": "^2.0.7"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"axios": "^1.6.0",
|
|
17
|
+
"express": "^4.18.2",
|
|
18
|
+
"serve-favicon": "^2.5.0",
|
|
19
|
+
"vue-server-renderer": "^2.7.14"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"vue-server-renderer": "^2.7.14"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [],
|
|
25
|
+
"author": "",
|
|
26
|
+
"license": "ISC",
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"registry": "https://registry.npmjs.org",
|
|
29
|
+
"access": "public"
|
|
30
|
+
}
|
|
31
|
+
}
|