Skip to content

zh/routing.md #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 28, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions zh/routing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# 路由和代码分割

## 使用 `vue-router` 的路由

你可能已经注意到,我们的服务器代码使用了一个 `*` 处理程序,它接受任意 URL。这允许我们将访问的 URL 传递到我们的 Vue 应用程序中,然后对客户端和服务器复用相同的路由配置!

为此,建议使用官方提供的 `vue-router`。我们首先创建一个文件,在其中创建 router。注意,类似于 `createApp`,我们也需要给每个请求一个新的 router 实例,所以文件导出一个 `createRouter` 函数:

```js
// router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export function createRouter () {
return new Router({
mode: 'history',
routes: [
// ...
]
})
}
```

然后更新 `app.js`:

```js
// app.js
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'
export function createApp () {
// 创建 router 实例
const router = createRouter()
const app = new Vue({
// 注入 router 到根 Vue 实例
router,
render: h => h(App)
})
// 注入 router 到根 Vue 实例
return { app, router }
}
```

现在我们需要在 `entry-server.js` 中实现服务器端路由逻辑(server-side routing logic):

```js
// entry-server.js
import { createApp } from './app'
export default context => {
// 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise,
// 以便服务器能够等待所有的内容在渲染前,
// 就已经准备就绪。
return new Promise((resolve, reject) => {
const { app, router } = createApp()
// 设置服务器端 router 的位置
router.push(context.url)
// 等到 router 将可能的异步组件和钩子函数解析完
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// 匹配不到的路由,执行 reject 函数,并返回 404
if (!matchedComponents.length) {
return reject({ code: 404 })
}
// Promise 应该 resolve 应用程序实例,以便它可以渲染
resolve(app)
}, reject)
})
}
```

假设服务器 bundle 已经完成构建(请再次忽略现在的构建设置),服务器用法看起来如下:

```js
// server.js
const createApp = require('/path/to/built-server-bundle.js')
server.get('*', (req, res) => {
const context = { url: req.url }
createApp(context).then(app => {
renderer.renderToString(app, (err, html) => {
if (err) {
if (err.code === 404) {
res.status(404).end('Page not found')
} else {
res.status(500).end('Internal Server Error')
}
} else {
res.end(html)
}
})
})
})
```

## 代码分割

应用程序的代码分割或惰性加载,有助于减少浏览器在初始渲染中下载的资源体积,可以极大地改善大体积 bundle 的可交互时间(TTI - time-to-interactive)。这里的关键在于,对初始首屏而言,"只加载所需"。

Vue 提供异步组件作为第一类的概念,将其与 [webpack 2 所支持的使用动态导入作为代码分割点](https://webpack.js.org/guides/code-splitting-async/)相结合,你需要做的是:

```js
// 这里进行修改……
import Foo from './Foo.vue'
// 改为这样:
const Foo = () => import('./Foo.vue')
```

如果你正在构建纯客户端 Vue 应用程序,这将在任何情况下运行。但是,在 SSR 中使用时有一些限制。首先,你需要在启动渲染之前先在服务器上解析所有的异步组件,否则你将在标记中获得一个空的占位符。在客户端,你还需要在开始混合之前执行此操作,否则客户端将遇到内容不匹配错误。

这使得在应用程序中的任意位置使用异步组件变得有点棘手(我们将来可能会改进这一点)。但是,**如果你在路由级别完成以上,可以实现无缝运行**(即在路由配置中使用异步组件),因为在解析路由时,`vue-router` 将自动解析匹配的异步组件。你需要做的是确保在服务器和客户端上都使用 `router.onReady`。我们已经在我们的服务器入口(server entry)中实现过了,现在我们只需要更新客户端入口(client entry):

```js
// entry-client.js
import { createApp } from './app'
const { app, router } = createApp()
router.onReady(() => {
app.$mount('#app')
})
```

异步路由组件的路由配置示例:

```js
// router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export function createRouter () {
return new Router({
mode: 'history',
routes: [
{ path: '/', component: () => import('./components/Home.vue') },
{ path: '/item/:id', component: () => import('./components/Item.vue') }
]
})
}
```