以 express + vue 为例,实现分为两步
1.服务端
const path = require('path')
const fs = require('fs')
const express = require('express')
const app = express()
const port = 3000
app.use('*', (req, res) => {
const url = req.originalUrl
console.log('url===>', url)
let html = fs.readFileSync(path.resolve('./index.html'), 'utf-8')
html = html.replace('__time__', new Date())
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
app.use 方法的第一个参数支持 4 种类型:
- A string representing a path. 代表路径的字符串
- A path pattern. 路径模式匹配
- A regular expression pattern to match paths. 匹配路径的正则表达式
- An array of combinations of any of the above. 以上3种的任意组合数组
以上代码使得访问应用的任何地址都会渲染 index.html,在真实 ssr 环境中,在响应 html 之前会调用事先写好的 entry-server.js 中导出的 render 方法:
2.客户端
跳转使用编程式导航 <router-link> 或 router.push\replace 等操作。
router-link 组件本质上应该时对 a 链接的封装,并对 a 链接进行点击拦截,如果不拦截走的还是服务器渲染即走进了 app.use('*', function() { ... }) 中间件函数。在拦截之后则实现了客户端的 csr 渲染。
完整 demo 如下:
//server.js
const path = require('path')
const fs = require('fs')
const express = require('express')
const app = express()
const port = 3000
app.use('*', (req, res) => {
const url = req.originalUrl
console.log('url===>', url)
let html = fs.readFileSync(path.resolve('./index.html'), 'utf-8')
html = html.replace('__time__', new Date())
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<a href="/" onclick="routeTo()">Home</a>
<a href="/about" onclick="routeTo()">about</a>
<div id="app">
<h1>hello express~~~~ __time__</h1>
<p>
<!--使用 router-link 组件进行导航 -->
<!--通过传递 `to` 来指定链接 -->
<!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
<router-link to="/">Go to Home</router-link>
<router-link to="/about">Go to About</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
</body>
<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/vue-router@4"></script>
<script>
// 1. 定义路由组件.
// 也可以从其他文件导入
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }
// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
// 我们后面再讨论嵌套路由。
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
]
// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = VueRouter.createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: VueRouter.createWebHistory(),
routes, // `routes: routes` 的缩写
})
// 5. 创建并挂载根实例
const app = Vue.createApp({})
//确保 _use_ 路由实例使
//整个应用支持路由。
app.use(router)
app.mount('#app')
//__________________________________________________________
function routeTo (event) {
event = event || window.event
event.preventDefault()
console.log(event)
console.log('渲染对应页面')
}
</script>
</html>