以下是我在这些资料中发现的一些问题:
很多关于你应该拥有的信息的假设,比如webpack配置,连接vue路由器的正确方法等等。
缺少某些重要的信息,给读者留下了一些空白。
在给出的示例中,大多数都没有遵循官方文档提供的标准和最佳实践。
本文的目的是提供使SSR与Vue路由器一起工作可能需要的所有信息,并尽力避免任何可能在以后给您带来麻烦的漏洞。我也尽量尊重Vue团队的所有建议。
途径
在进入实际实现之前,有一些主要的概念你需要了解:
SSR包括为服务器上请求的路由创建一个完全加载的应用程序版本。一旦该页面在客户端呈现,客户端代码就拥有了所有权。
您的应用程序将需要两个入口构建点,一个用于服务器,一个用于客户机。
考虑到这一点,我们将在本文中完成以下工作:
安装所需的依赖项
Webpack配置
NPM构建脚本
文件夹结构
应用程序配置
设置Vue路由器
客户端入口点
服务器入口点
服务器配置
让我们希望这个例子能使主题更加清晰!
依赖关系
让我们来看看我们将要安装的依赖项:
1、我们将使用一个模板,它已经为VueJS应用程序提供了基本的Webpack配置。我们还需要安装vue-cli:
#install vue-cli npm install -g vue-cli #create project using webpack-simple vue init webpack-simple vue-ssr
现在我们需要安装webpack-simple模板的所有依赖项。在此之前,我们没有做过任何与SSR相关的事情;我们只是在建立一个通用的VueJS环境。
#go to project folder cd vue-cli #install dependencies npm install
2、现在我们有了一个VueJS项目,可以开始添加SSR配置了。在此之前,我们需要添加三个依赖项,它们都与SSR相关。
#install vue-server-render, vue-router, express and webpack-merge npm install vue-server-renderer vue-router express webpack-merge --save
vue-server-render
:用于SSR的Vue库。vue-router
:Vue library for SPA.express
:我们需要运行NodeJS服务器。webpack-merge
:我们将使用它来合并webpack配置。
Webpack配置
我们将需要两个Webpack配置,一个用于构建客户机条目文件,另一个用于构建服务器条目文件。
让我们先看看webpack客户端配置,它也将是服务器条目配置的基本webpack配置。我们将使用我们安装的模板附带的条目,只是我们将条目更改为entry-client.js。
var path = require('path') var webpack = require('webpack') module.exports = { entry: './src/entry-client.js', output: { path: path.resolve(__dirname, './dist'), publicPath: '/dist/', filename: 'build.js' }, module: { rules: [ { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ], }, { test: /\.scss$/, use: [ 'vue-style-loader', 'css-loader', 'sass-loader' ], }, { test: /\.sass$/, use: [ 'vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax' ], }, { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { // Since sass-loader (weirdly) has SCSS as its default parse mode, we map // the "scss" and "sass" values for the lang attribute to the right configs here. // other preprocessors should work out of the box, no loader config like this necessary. 'scss': [ 'vue-style-loader', 'css-loader', 'sass-loader' ], 'sass': [ 'vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax' ] } // other vue-loader options go here } }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { name: '[name].[ext]?[hash]' } } ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' }, extensions: ['*', '.js', '.vue', '.json'] }, devServer: { historyApiFallback: true, noInfo: true, overlay: true }, performance: { hints: false }, devtool: '#eval-source-map' } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' // http://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }
现在让我们添加服务器webpack配置:
var path = require('path') var webpack = require('webpack') var merge = require('webpack-merge') var baseWebpackConfig = require('./webpack.config') var webpackConfig = merge(baseWebpackConfig, { target: 'node', entry: { app: './src/entry-server.js' }, devtool: false, output: { path: path.resolve(__dirname, './dist'), filename: 'server.bundle.js', libraryTarget: 'commonjs2' }, externals: Object.keys(require('./package.json').dependencies), plugins: [ new webpack.DefinePlugin({ 'process.env': 'production' }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] }) module.exports = webpackConfig
这里没有什么奇怪的,只有两件事:条目是entry-server.js
,对于输出,我们使用commonjs
作为库目标。
这就是Webpack配置。现在让我们看看用package.json构建应用程序的脚本。
package.json生成脚本
你可以根据自己的需要进行修改,但启动应用程序需要执行以下三个步骤:
您需要构建客户机条目
您需要构建服务器条目
您需要启动服务器
"scripts": { "start": "npm run build && npm run start-server", "build": "npm run build-client && npm run build-server", "build-client": "cross-env NODE_ENV=production webpack --progress --hide-modules", "build-server": "cross-env NODE_ENV=production webpack --config webpack.server.config.js --progress --hide-modules", "start-server": "node server.js" }
在配置中,我们使用的start
脚本将运行我们刚刚提到的三个步骤。但是,如果需要,我们还设置了单独运行它们的脚本。
文件夹结构
dist文件夹是由webpack在构建时创建的。
node_modules文件夹…你知道这是干嘛的。
src包含我们的vue应用程序。在里面,您将找到服务器和客户端入口点、vue main.js文件、应用程序组件、其他组件的文件夹(我们有home和about组件)、包含路由器配置的路由器文件夹,最后是assets文件夹。
.babelrc、.gitignore、packages.json…你可能知道它们是什么。
index.html是我们应用程序的主要html。
server.js是服务器配置和启动文件。
最后,介绍两个webpack配置文件。
Index HTML
这是我们的主要HTML文件。
<!doctype html> <html> <head> <!-- use triple mustache for non-HTML-escaped interpolation --> {{{ meta }}} <!-- use double mustache for HTML-escaped interpolation --> <title>{{ title }}</title> </head> <body> <!--vue-ssr-outlet--> <script src="dist/build.js"></script> </body> </html>
有几件事需要讨论:
我在模板中添加了一些插值来填充来自服务器的数据。这是vue ssr的一个特性,我稍后将展示。
我们加载build.js,它是由Webpack生成的客户端包。
App.vue元件
这个组件是我们app的根组件,它有几个功能:
配置带有Vue路由器链接的菜单。
设置要呈现路由组件的容器。
使用id应用程序设置元素,该应用程序将用于安装应用程序的客户端部分。
<template> <div id="app"> Hello World! <p> <router-link to="/">Go To Home</router-link> <router-link to="/about">Go To About</router-link> </p> <router-view></router-view> </div> </template> <script> export default { }; </script>
路由器文件配置
因为我们的应用程序将在服务器上启动,所以我们需要为每个服务器请求提供一个新的路由器实例。在路由器文件夹中,我们将有一个带有路由器配置的文件。
// router.js import Vue from 'vue'; import Router from 'vue-router'; import Home from '../components/Home.vue'; import About from '../components/About.vue'; Vue.use(Router); export function createRouter () { return new Router({ mode: 'history', routes: [ { path: '/', component: Home }, { path: '/about', component: About } ] }); }
让我们看一下代码:
我们导入所需的所有依赖项。
我们告诉Vue使用Vue路由器。
我们导出一个提供路由器配置新实例的函数。
我们在历史模式中实例化路由器,并声明要处理的两条路由。
主视图文件配置
就像我们需要提供一个新的路由器实例一样,我们也需要提供一个新的app实例。这个文件负责启动路由器和根应用程序组件。服务器入口点和客户机入口点都将使用这个文件。
// main.js import Vue from 'vue' import App from './App.vue' import { createRouter } from './router/router.js' // export a factory function for creating fresh app, router and store // instances export function createApp() { // create router instance const router = createRouter(); const app = new Vue({ router, // the root instance simply renders the App component. render: h => h(App) }); return { app, router }; }
让我们来看看代码:
我们导入所需的所有依赖项。
我们导出一个函数,该函数提供应用程序和路由器的新实例。
我们使用之前在
router.js
文件中看到的方法实例化路由器。我们使用路由器和呈现函数创建一个新的应用程序实例,并传递根应用程序组件。
我们返回两个实例。
客户端入口点
这段代码非常简单。这是Webpack客户机构建配置的入口文件。
//client-entry.js import { createApp } from './main.js'; const { app } = createApp() // this assumes App.vue template root element has `id="app"` app.$mount('#app')
让我们来看看代码:
我们导入所需的所有依赖项。
我们从
main.js
文件创建应用程序并保存应用程序实例。我们在ID设置为app的节点中装载应用程序。在本例中,包含该id的节点是app.vue组件模板的根元素。
服务器入口点
这个文件是webpack服务器构建的入口点。该构建的结果就是我们稍后配置服务器时要针对的目标。
//server-entry.js import { createApp } from './main.js'; export default context => { // since there could potentially be asynchronous route hooks or components, // we will be returning a Promise so that the server can wait until // everything is ready before rendering. return new Promise((resolve, reject) => { const { app, router } = createApp(); // set server-side router's location router.push(context.url); // wait until router has resolved possible async components and hooks router.onReady(() => { const matchedComponents = router.getMatchedComponents(); // no matched routes, reject with 404 if (!matchedComponents.length) { return reject({ code: 404 }); } // the Promise should resolve to the app instance so it can be rendered resolve(app); }, reject); }); }
让我们来看看代码:
我们导入所需的所有依赖项。
我们导出一个函数,该函数接收上下文作为参数。
函数返回一个promise。
我们从main.js create app函数实例化应用程序和路由器。
我们从上下文获取当前URL(这将由服务器提供),以便将正确的URL推送到路由器。
一旦路由器就绪,我们检查路由是否匹配上下文URL。如果是,我们解析promise并返回app实例。否则,我们拒绝承诺。
配置和启动服务器
我们几乎把一切都准备好了。唯一缺少的是配置和启动express
服务器。
//server.js const express = require('express'); const server = express(); const fs = require('fs'); const path = require('path'); //obtain bundle const bundle = require('./dist/server.bundle.js'); //get renderer from vue server renderer const renderer = require('vue-server-renderer').createRenderer({ //set template template: fs.readFileSync('./index.html', 'utf-8') }); server.use('/dist', express.static(path.join(__dirname, './dist'))); //start server server.get('*', (req, res) => { bundle.default({ url: req.url }).then((app) => { //context to use as data source //in the template for interpolation const context = { title: 'Vue JS - Server Render', meta: ` <meta description="vuejs server side render"> ` }; renderer.renderToString(app, context, function (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) } }); }, (err) => { console.log(err); }); }); server.listen(8080);
哇!你以前觉得太过分了让我们深入研究代码,看看发生了什么。
我们导入
express
来创建服务器。我们还导入了一些NodeJS功能。我们导入服务器包,这是Webpack服务器构建的结果。
我们导入
vue-server-renderer
库并创建renderer,为模板提供index.html
位置。我们配置了
express
路径。我们启动服务器。
这个bundle是使用Webpack构建
servlet -entry.js
的结果,因此我们可以使用默认函数来接收上下文作为带有URL的参数。因为它是一个承诺,所以我们设置了一个成功和错误回调。
成功回调做了很多事情,让我们来看看:
我们使用将要在
index.html
中插入的数据创建一个const(我们之前在index.html中看到过插值)。我们调用renderer的render to string函数,该函数接收应用程序(由已解析的promise返回),以及我们刚刚创建的上下文(用于索引中的插值…这是可选的),如果一切正常,回调函数。
render to string回调函数检查任何错误,如果没有,它只将生成的HTML作为响应发送。
最后,我们开始监听端口8080。
现在,如果您运行脚本启动并在浏览器中打开localhost:8080,您将看到一个带有vue-router
的工作SSR
。
我不认为我需要说这是很多配置使事情工作,但一旦它完成,你不会去碰它很多。只要确定SSR是你需要的。
以上就是使用vue路由器进行vue.js服务器端渲染:分步指南的详细内容,更多请关注0133技术站其它相关文章!