vue/vue-cli+express手把教你搭建SSR

最近简单的研究了一下SSR,对SSR已经有了一个简单的认知,主要应用于单页面应用,Nuxt是SSR很不错的框架。也有过调研,简单的用了一下,感觉还是很不错。但是还是想知道若不依赖于框架又应该如果处理SSR,研究一下做个笔记。

什么是SSR

把Vue组件渲染为服务器端的HTML字符串,将他们直接发送到浏览器,最后将静态标记混合为客户端上完全交互的应用程序。

为什么要使用SSR

  • 更好的SEO,搜索引擎爬虫爬取工具可以直接查看完全渲染的页面
  • 更宽的内容达到时间(time-to-content),当权请求页面的时候,服务端渲染完数据之后,把渲染好的页面直接发送给浏览器,并进行渲染。浏览器只需要解析html不需要去解析js。

SSR弊端

  1. 开发条件受限,Vue组件的某些生命周期钩子函数不能使用
  2. 开发环境基于Node.js
  3. 会造成服务端更多的负载。在Node.js中渲染完整的应用程序,显然会比仅仅提供静态文件server更加占用CPU资源,因此如果你在预料在高流量下使用,请准备响应的服务负载,并明智的采用缓存策略。

准备工作

在正式开始之前,在vue官网找到了一张这个图片,图中详细的讲述了vue中对ssr的实现思路。如下图简单的说一下。

下图中很重要的一点就是webpack,在项目过程中会用到webpack的配置,从最左边开始就是我们所写入的源码文件,所有的文件都有一个公共的入口文件app.js,然后就进入了server-entry(服务端入口)和client-entry(客户端入口),两个入口文件都要经过webpack,当访问node端的时候,使用的是服务端渲染,在服务端渲染的时候,会生成一个server-Bender,最后通过server-Bundle可以渲染出HTML页面,若在客户端访问的时候则是使用客户端渲染,通过client-Bundle在以后渲染出HTML页面。so~通过这个图可以很清晰的看出来,接下来会用到两个文件,一个server入口,一个client入口,最后由webpack生成server-Bundle和client-Bundle,最终当去请求页面的时候,node中的server-Bundle会生成HTML界面通过client-Bundle混合到html页面中即可。

对于vue中使用ssr做了一些简单的了解之后,那么就开始我们要做的第一步吧,首先要创建一个项目,创建一个文件夹,名字不重要,但是最好不要使用中文。

mkdir dome
cd dome
npm init

npm init命令用来初始化package.json文件:

{
 "name": "dome",  // 项目名称
 "version": "1.0.0",  // 版本号
 "description": "",  // 描述
 "main": "index.js",  // 入口文件
 "scripts": {     // 命令行执行命令 如:npm run test
  "test": "echo \"Error: no test specified\" && exit 1"
 },
 "author": "Aaron",   // 作者
 "license": "ISC"   // 许可证
}

初始化完成之后接下来需要安装,项目所需要依赖的包,所有依赖项如下:

npm install express --save-dev
npm install vue --save-dev
npm install vue-server-render --save-dev
npm install vue-router --save-dev

如上所有依赖项一一安装即可,安装完成之后就可以进行下一步了。前面说过SSR是服务端预渲染,所以当然要创建一个Node服务来支撑。在dome文件夹下面创建一个index.js文件,并使用express创建一个服务。

代码如下:

const express = require("express");
const app = express();

app.get('*',(request,respones) => {
  respones.end("ok");
})

app.listen(3000,() => {
  console.log("服务已启动")
});

完成上述代码之后,为了方便我们需要在package.json添加一个命令,方便后续开发启动项目。

{
 "scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "start": "node index.js"
 }
}

创建好之后,在命令行直接输入npm start即可,当控制台显示服务已启动则表示该服务已经启动成功了。接下来需要打开浏览器看一下渲染的结果。在浏览器地址栏输入locahost:3000则可以看到ok两个字。

SSR渲染手动搭建

前面的准备工作已经做好了,千万不要完了我们的主要目的不是为了渲染文字,主要的目标是为了渲染*.vue文件或html所以。接下来就是做我们想要做的事情了。接下来就是要修改index.js文件,将之前安装的`vue
和vue-server-render`引入进来。

由于返回的不再是文字,而是html模板,所以我们要对响应头进行更改,告诉浏览器我们渲染的是什么,否则浏览器是不知道该如何渲染服务器返回的数据。

在index.js中引入了vue-server-render之后,在使用的时候,我们需要执行一下vue-server-render其中的creteRender方法,这个方法的作用就是会将vue的实例转换成html的形式。

既然有了vue-server-render的方法,接下来就需要引入主角了vue,引入之后然后接着在下面创建一个vue实例,在web端使用vue的时候需要传一些参数给Vue然而在服务端也是如此也可以传递一些参数给Vue实例,这个实例也就是后续添加的那些*.vue文件。为了防止用户访问的时候页面数据不会互相干扰,暂时需要把实例放到get请求中,每次有访问的时候就会创建一个新的实例,渲染新的模板。

creteRender方法能够把vue的实例转成html字符串传递到浏览器。那么接下来由应该怎么做?在vueServerRender方法下面有一个renderToString方法,这个方法就可以帮助我们完成这步操作。这个方法接受的第一个参数是vue的实例,第二个参数是一个回调函数,如果不想使用回调函数的话,这个方法也返回了一个Promise对象,当方法执行成功之后,会在then函数里面返回html结构。

改动如下:

const express = require("express");
const Vue = require("vue");
const vueServerRender = require("vue-server-render").creteRender();

const app = express();

app.get('*',(request,respones) => {
  const vueApp = new Vue({
    data:{
      message:"Hello,Vue SSR!"
    },
    template:`<h1>{{message}}</h1>`
  });
  respones.status(200);
  respones.setHeader("Content-Type","text/html;charset-utf-8;");
  vueServerRender.renderToString(vueApp).then((html) => {
    respones.end(html);
  }).catch(error => console.log(error));
})

app.listen(3000,() => {
  console.log("服务已启动")
});

上述操作完成之后,一定要记得保存,然后重启服务器,继续访问一下locahost:3000,就会看到在服务端写入的HTML结构了。这样做好像给我们添加了大量的工作,到底与在web端直接使用有什么区别么?

接下来见证奇迹的时刻到了。在网页中右键查看源代码就会发现与之前的在web端使用的时候完全不同,可以看到渲染的模板了。如果细心的就会发现一件很有意思的事情,在h1标签上会有一个data-server-rendered=true这样的属性,这个可以告诉我们这个页面是通过服务端渲染来做的。大家可以去其他各大网站看看哦。没准会有其他的收获。

上面的案例中,虽然已经实现了服务端预渲染,但是会有一个很大的缺陷,就是我们所渲染的这个网页并不完整,没有文档声明,head等等等,当然可能会有一个其他的想法,就是使用es6的模板字符串做拼接就好了啊。确实,这样也是行的通的,但是这个仍是饮鸩止渴不能彻底的解决问题,如果做过传统MVC开发的话,就应该知道,MVC开发模式全是基于模板的,现在这种与MVC有些相似的地方,同理也是可以使用模板的。在dome文件夹下创建index.html,并创建好HTML模板。

模板现在有了该如何使用?在creteRender函数可以接收一个对象作为配置参数。配置参数中有一项为template,这项配置的就是我们即将使用的Html模板。这个接收的不是一个单纯的路径,我们需要使用fs模块将html模板读取出来。

其配置如下:

let path = require("path");
const vueServerRender = require("vue-server-render").creteRender({
  template:require("fs").readFileSync(path.join(__dirname,"./index.html"),"utf-8")
});

现在模板已经有了,在web端进行开发的时候,需要挂在一个el的挂载点,这样Vue才知道把这些template渲染在哪,服务端渲染也是如此,同样也需要告诉Vue将template渲染到什么地方。接下来要做的事情就是在index.html中做手脚。来通知creteRender把template添加到什么地方。

更改index.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
  <!--vue-ssr-outlet-->
</body>
</html>

可以发现,在html的body里面添加了一段注释,当将vueServerRender编译好的html传到模板当中之后这个地方将被替换成服务端预编译的模板内容,这样也算是完成一个简单的服务端预渲染了。虽然写入的只是简单的html渲染,没有数据交互也没有页面交互,也算是一个不小的进展了。

使用SSR搭建项目我们继续延续上个项目继续向下开发,大家平时在使用vue-cli搭建项目的时候,都是在src文件夹下面进行开发的,为了和vue项目结构保持一致,同样需要创建一个src文件夹,并在src文件夹创建conponents,router,utils,view,暂定项目结构就这样,随着代码的编写会逐渐向项目里面添加内容。

└─src
|  ├─components
|  ├─router
|  ├─utils
|  ├─view
|  └─app.js
└─index.js

初始的目录结构已经搭建好了之后,接下来需要继续向下进行,首先要做的就是要在router目录中添加一个index.js文件,用来创建路由信息(在使用路由的时候一定要确保路由已经安装)。路由在项目中所起到的作用应该是重要的,路由会通过路径把页面和组件之间建立联系,并且一一的对应起来,完成路由的渲染。

接下来在router下面的index.js文件中写入如下配置:

const vueRouter = require("vue-router");
const Vue = require("vue");

Vue.use(vueRouter);

modul.exports = () => {
  return new vueRouter({
    mode:"history",
    routers:[
      {
        path:"/",
        component:{
          template:`<h1>这里是首页<h1/>`
        },
        name:"home"
      },
      {
        path:"/about",
        component:{
          template:`<h1>这里是关于页<h1/>`
        },
        name:"about"
      }
    ]
  })
}

上面的代码中,仔细观察的话,和平时在vue-cli中所导出的方式是不一样的,这里采用了工厂方法,这里为什么要这样?记得在雏形里面说过,为了保证用户每次访问都要生成一个新的路由,防止用户与用户之间相互影响,也就是说Vue实例是新的,我们的vue-router的实例也应该保证它是一个全新的。

现在Vue实例和服务端混在一起,这样对于项目的维护是很不好的,所以也需要把Vue从服务端单独抽离出来,放到app.js中去。这里采用和router同样的方式使用工厂方式,以保证每次被访问都是一个全新的vue实例。在app.js导入刚刚写好的路由,在每次触发工厂的时候,创建一个新的路由实例,并绑定到vue实例里面,这样用户在访问路径的时候无论是vue实例还是router都是全新的了。

app.js:

const Vue = require("vue");
const createRouter = require("./router")

module.exports = (context) => {
  const router = createRouter();
  return new Vue({
    router,
    data:{
      message:"Hello,Vue SSR!"
    },
    template:`
      <div>
        <h1>{{message}}</h1>
        <ul>
          <li>,
            <router-link to="/">首页<router-link/>
          </li>
          <li>
            <router-link to="/about">关于我<router-link/>
          </li>
        </ul>
      </div>
      <router-view></router-view>
    `
  });
}

做完这些东西貌似好像就能用了一样,但是还是不行,仔细想想好像忘了一些什么操作,刚刚把vue实例从index.js中抽离出来了,但是却没有在任何地方使用它,哈哈,好像是一件很尴尬的事情。

修改index.js文件:

const express = require("express");
const vueApp = require("./src/app.js");
const vueServerRender = require("vue-server-render").creteRender();

const app = express();

app.get('*',(request,respones) => {

  // 这里可以传递给vue实例一些参数
  let vm = vueApp({})

  respones.status(200);
  respones.setHeader("Content-Type","text/html;charset-utf-8;");
  vueServerRender.renderToString(vm).then((html) => {
    respones.end(html);
  }).catch(error => console.log(error));
})

app.listen(3000,() => {
  console.log("服务已启动")
});

准备工作都已经做好啦,完事具备只欠东风啦。现在运行一下npm start可以去页面上看一下效果啦。看到页面中已经渲染出来了,但是好像是少了什么?虽然导航内容已经都显示出来了,但是路由对应的组件好像没得渲染噻。具体是什么原因导致的呢,vue-router是由前端控制渲染的,当访问路由的时候其实,在做首屏渲染的时候并没有授权给服务端让其去做渲染路由的工作。(⊙﹏⊙),是的我就是这么懒...

这个问题解决方案也提供了相对应的操作,不然就知道该怎么写下去了。既然在做渲染的时候分为服务端渲染和客户端渲染两种,那么我们就需要两个入口文件,分别对应的服务端渲染的入口文件,另个是客户端渲染的入口文件。

在src文件夹下面添加两个.js文件(当然也可以放到其他地方,这里只是为了方便),entry-client.js这个文件用户客户端的入口文件,entry-server.js那么这个文件则就作为服务端的入口文件。既然入口文件已经确定了,接下来就是要解决刚才的问题了,首先解决的是服务端渲染,在服务端这里需要把用户所访问的路径传递给vue-router,如果不传递给vue-router的话,vue-router会一脸懵逼的看着你,你什么都不给我,我怎么知道渲染什么?

在entry-server中需要做的事情就是需要把app.js导入进来,这里可以向上翻一下app.js中保存的是创建vue实例的方法。首先在里面写入一个函数,至于为什么就不多说了(同样也是为了保证每次访问都有一个新的实例),这个函数接收一个参数([object]),由于这里考虑到可能会有异步操作(如懒加载),在这个函数中使用了Promise,在Promise中首先要拿到连个东西,不用猜也是能想到的,很重要的vue实例和router实例,so~但是在app中好像只导出了vue实例,还要根据当前所需要的去更改app.js。

app.js:

const Vue = require("vue");
const createRouter = require("./router")

module.exports = (context) => {
  const router = createRouter();
  const app = new Vue({
    router,
    data:{
      message:"Hello,Vue SSR!"
    },
    template:`
      <div>
        <h1>{{message}}</h1>
        <ul>
          <li>,
            <router-link to="/">首页<router-link/>
          </li>
          <li>
            <router-link to="/about">关于我<router-link/>
          </li>
        </ul>
      </div>
      <router-view></router-view>
    `
  });
  return {
    app,
    router
  }
}

通过上面的改造之后,就可以在entry-server.js中轻松的拿到vue和router的实例了,现在查看一下当前entry-server.js中有那些可用参数,vue,router,提及到的URL从哪里来?既然这个函数是给服务端使用的,那么当服务端去执行这个函数的时候,就可以通过参数形式传递进来,获取到我们想要的参数,我们假设这个参数叫做url,我们需要让路由去做的就是跳转到对应的路由中(这一步很重要),然后再把对router的实例挂载到vue实例中,然后再把vue实例返回出去,供vueServerRender消费。那么就需要导出这个函数,以供服务端使用。

由于我们不能预测到用户所访问的路由就是在vue-router中所配置的,所以需要在onReady的时候进行处理,我们可以通过router的getMatchedComponents这个方法,获取到我们所导入的组件,这些有个我们就可通过判断组件对匹配结果进行渲染。

entry-server.js

const createApp = require("./app.js");

model.exports = (context) => {
  return new Promise((reslove,reject) => {
    let {url} = context;
    let {app,router} = createApp(context);
    router.push(url);
    // router回调函数
    // 当所有异步请求完成之后就会触发
    router.onRady(() => {
      let matchedComponents = router.getMatchedComponents();
      if(!matchedComponents.length){
        return reject({
          code:404,
        });
      }
      reslove(app);
    },reject)
  })
}

既然实例又发生了变化,需要对应发生变化的index.js同样也需要做出对应的改动。把刚才的引入vue实例的路径改为entey-server.js,由于这里返回的是一个Promise对象,这里使用async/await处理接收一下,并拿到vue实例。不要忘了把router所需要的url参数传递进去。

index.js:

const express = require("express");
const App = require("./src/entry-server.js");
const vueServerRender = require("vue-server-render").creteRender();

const app = express();

app.get('*',async (request,respones) => {
  respones.status(200);
  respones.setHeader("Content-Type","text/html;charset-utf-8;");

  let {url} = request;
  // 这里可以传递给vue实例一些参数
  let vm = await App({url});
  vueServerRender.renderToString(vm).then((html) => {
    respones.end(html);
  }).catch(error => console.log(error));
})

app.listen(3000,() => {
  console.log("服务已启动")
});

这下子就完成了,启动项目吧,当访问根路径的时候,就会看到刚才缺少的组件也已经渲染出来了,当然我们也可以切换路由,也是没有问题的。大功告成。。。好像并没有emmmmmmmmm,为什么,细心的话应该会发现,当我们切换路由的时候,地址栏旁边的刷新按钮一直在闪动,这也就是说,我们所做出来的并不是一个单页应用(手动笑哭),出现这样的问题也是难怪的,毕竟我们没有配置前端路由,我们把所有路由的控制权都交给了服务端,每次访问一个路由的时候,都会向服务端发送一个请求,返回路由对应的页面。想要解决这个问题,当处于前端的时候我们需要让服务端把路由的控制权交还给前端路由,让前端去控制路由的跳转。

之前在src文件夹下面添加了两个文件,只用到了服务端的文件,为了在客户端能够交还路由控制权,要对web端路由进行配置。由于在客户端在使用vue的时候需要挂载一个document,因为vue的实例已经创建完成了,所以,这里需要使用$mount这个钩子函数,来完成客户端的挂载。同样为了解决懒加载这种类似的问题so~同样需要使用onReady里进行路由的处理,只有当vue-router加载完成以后再去挂载。

在客户端是使用的时候很简单,只需要把路由挂载到app里面就可以了。

entry-client.js

const createApp = require("./app.js");
let {app,router} = createApp({});

router.onReady(() => {
  app.$mount("#app")
});

整个项目的雏形也就这样了,由于服务端把路由控制权交还给客户端,需要复杂的webpack配置,so~不再赘述了,下面直接使用vue-cli继续(做的是使用需要用到上面的代码)。

vue-cli项目搭建

在做准备工作的时候简单讲述了vue中使用ssr的运行思路,里面提及了一个很重要的webpack,因此这里需要借助vue-cli脚手架,直接更改原有的webpack就可以了,这样会方便很多。

这里建议大家返回顶部再次看一下vue服务端渲染的流程,在介绍中的client-bundle和server-bundle,,所以需要构建两个配置,分别是服务端配置和客户端的配置。

如想要实现服务端渲染需要对vue-cli中个js文件中的配置进行修改。以下只展示更改部分的代码,不展示全部。

文件分别是:

  • webpack.server.conf.js - 服务端webpack配置
  • dev-server.js - 获取服务端bundle
  • server.js - 创建后端服务
  • webpack.dev.conf.js - 客户端的bundle
  • webpack.base.conf - 修改入口文件

客户端配置

客户端生成一份客户端构建清单,记录客户端的资源,最终会将客户端构建清单中记录的文件,注入到执行的执行的模板中,这个清单与服务端类似,同样也会生成一份json文件,这个文件的名字是vue-ssr-client-manifest.json(项目启动以后可以通过地址/文件名访问到),当然必不可少的是,同样也需要引入一个叫做vue-server-renderer/client-plugin模块,作为webpack的插件供其使用。

首先要安装一下vue-server-render这个模块,这个是整个服务端渲染的核心,没有整个ssr是没有任何灵魂的。

npm install vue-server-render -S

安装完成之后,首先要找到webpack.dev.conf.js,首先要对其进行相关配置。

webpack.dev.conf.js

// 添加引入 vue-server-render/client-plugin 模块
const vueSSRClientPlugin = require("vue-server-render/client-plugin");

const devWebpackConfig = merge(baseWebpackConfig,{
  plugins:[
    new vueSSRClientPlugin()
  ]
});

添加了这个配置以后,重新启动项目通过地址就可以访问到vue-ssr-client-manifest.json(http://localhost:8080/vue-ssr-client-manifest.json),页面中出现的内容就是所需要的client-bundle。

服务端配置

服务端会默认生成一个vue-ssr-server-bundle.json文件,在文件中会记录整个服务端整个输出,怎么才能生成这个文件呢?要在这个json文件,必须要引入vue-server-renderer/server-plugin,并将其作为webpack的插件。

在开始服务端配置之前,需要在src文件夹下面创建三个文件,app.js,entry-client.js,entry-server.js,创建完成之后需要对其写入相关代码。

src/router/index.js

import vueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "@/components/HelloWorld");

Vue.use(vueRouter);
export default () => {
  return new vueRouter({
    mode:"history",
    routers:[
      {
        path:"/",
        component:HelloWorld,
        name:"HelloWorld"
      }
    ]
  })
}

app.js

import Vue from "vue";
import createRouter from "./router";
import App from "./App.vue";

export default (context) => {
  const router = createRouter();
  const app = new Vue({
    router,
    component:{App},
    template:"<App/>"
  });
  return {
    app,
    router
  }
}

entry-server.js

import createApp from "./app.js";

export default (context) => {
  return new Promise((reslove,reject) => {
    let {url} = context;
    let {app,router} = createApp(context);
    router.push(url);
    router.onRady(() => {
      let matchedComponents = router.getMatchedComponents();
      if(!matchedComponents.length){
        return reject({
          code:404,
        });
      }
      reslove(app);
    },reject)
  })
}

entry-client.js

import createApp from "./app.js";
let {app,router} = createApp();

router.onReady(() => {
  app.$mount("#app");
});
webpack.base.conf.js

module.exports = {
  entry:{
    app:"./src/entry-client.js"
  },
  output:{
    publicpath:"http://localhost:8080/"
  }
};

webpack.server.conf.js(手动创建)

const webpack = require("webpack");
const merge = require("webpack-merge");
const base = require("./webpack.base.conf");
// 手动安装
// 在服务端渲染中,所需要的文件都是使用require引入,不需要把node_modules文件打包
const webapckNodeExternals = require("webpack-node-externals");

const vueSSRServerPlugin = require("vue-server-render/server-plugin");

module.exports = merge(base,{
  // 告知webpack,需要在node端运行
  target:"node",
  entry:"./src/entry-server.js",
  devtool:"source-map",
  output:{
    filename:'server-buldle.js',
    libraryTarget: "commonjs2"
  },
  externals:[
    webapckNodeExternals()
  ],
  plugins:[
    new webapck.DefinePlugin({
      'process.env.NODE_ENV':'"devlopment"',
      'process.ent.VUE_ENV': '"server"'
    }),
    new vueSSRServerPlugin()
  ]
});

dev-server.js(手动创建)

const serverConf = require("./webpack.server.conf");
const webpack = require("webpack");
const fs = require("fs");
const path = require("path");
// 读取内存中的.json文件
// 这个模块需要手动安装
const Mfs = require("memory-fs");
const axios = require("axios");

module.exports = (cb) => {
  const webpackComplier = webpack(serverConf);
  var mfs = new Mfs();

  webpackComplier.outputFileSystem = mfs;

  webpackComplier.watch({},async (error,stats) => {
    if(error) return console.log(error);
    stats = stats.toJson();
    stats.errors.forEach(error => console.log(error));
    stats.warnings.forEach(warning => console.log(warning));
    // 获取server bundle的json文件
    let serverBundlePath = path.join(serverConf.output.path,'vue-ssr-server-bundle.json');
    let serverBundle = JSON.parse(mfs.readFileSync(serverBundlePath,"utf-8"));
    // 获取client bundle的json文件
    let clientBundle = await axios.get("http://localhost:/8080/vue-ssr-client-manifest.json");
    // 获取模板
    let template = fs.readFileSync(path.join(__dirname,".."."index.html"),"utf-8");
    cb && cb(serverBundle,clientBundle,template);
  })
};

根目录/server.js(手动创建)

const devServer = require("./build/dev-server.js");
const express = require("express");
const app = express();
const vueRender = require("vue-server-render");

app.get('*',(request,respones) => {
  respones.status(200);
  respones.setHeader("Content-Type","text/html;charset-utf-8;");
  devServer((serverBundle,clientBundle,template) => {
    let render = vueRender.createBundleRenderer(serverBundle,{
      template,
      clientManifest:clientBundle.data,
      // 每次创建一个独立的上下文
      renInNewContext:false
    });
    render.renderToString({
      url:request.url
    }).then((html) => {
      respones.end(html);
    }).catch(error => console.log(error));
  });
})

app.listen(5000,() => {
  console.log("服务已启动")
});

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
  <div id="app">
    <!--vue-ssr-outlet-->
  </div>
  <!-- built files will be auto injected -->
</body>
</html>

以上就是所有要更改和添加的配置项,配置完所有地方就可以完成服务端渲染。此时需要在package.json中的sctipt中添加启动项:http:node server.js,就可以正常运行项目了。注意一定要去访问服务端设置的端口,同时要保证你的客户端也是在线的。

总结

这篇博客耗时3天才完成,可能读起来会很费时间,但是却有很大的帮助,希望大家能够好好阅读这篇文章,对大家有所帮助。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 通过vue-cli3构建一个SSR应用程序的方法

    1.前沿 1.1.什么是SSR SSR(服务端渲染)顾名思义就是将页面在服务端渲染完成后在客户端直接展示. 1.2.客户端渲染与服务端渲染的区别 传统的SPA模式 即客户端渲染的模式 Vue.js构建的应用程序,默认情况下是有一个html模板页,然后通过webpack打包生成一堆js.css等等资源文件.然后塞到index.html中 用户输入url访问页面 -> 先得到一个html模板页 -> 然后通过异步请求服务端数据 -> 得到服务端的数据 -> 渲染成局部页面 ->

  • 浅谈在不使用ssr的情况下解决Vue单页面SEO问题(2)

    上一篇说了vue单页面解决解决SEO的问题 只是用php预处理了meta标签 但是依然没有内容填充,所以对于内容抓取依然有些乏力,只是解决了从无到有的问题 那接下来可以更进一步的预填充内容了 预填充内容 这里依然使用php来实现 首先在php中拉取需要填充的数据,列表或是具体内容 修改拉取数据部分 $urlExp = explode('/',$_SERVER['REQUEST_URI']); if(count($urlExp)>2 && $urlExp[1] == 'article'

  • 详解Vue SSR( Vue2 + Koa2 + Webpack4)配置指南

    正如Vue官方所说,SSR配置适合已经熟悉 Vue, webpack 和 Node.js 开发的开发者阅读.请先移步ssr.vuejs.org了解手工进行SSR配置的基本内容. 从头搭建一个服务端渲染的应用是相当复杂的.如果您有SSR需求,对Webpack及Koa不是很熟悉,请直接使用NUXT.js. 本文所述内容示例在 Vue SSR Koa2 脚手架 : https://github.com/yi-ge/Vue-SSR-Koa2-Scaffold 我们以撰写本文时的最新版:Vue 2,Web

  • 从0到1构建vueSSR项目之node以及vue-cli3的配置

    前言 上一次做了路由的相关配置,原本计划今天要做vuex部分,但是想了想,发现vuex单独的客户端部分穿插解释起来很麻烦,所以今天改做服务端部分. 服务端部分做完,再去做vuex的部分,这样就会很清晰. vue ssr是分两个端,一个是客户端,一个是服务端. 所以要做两个cli3的配置. 那么下面就直接开始做吧. 修改package.json的命令 //package.json :client代表客户端 :server代表服务端 //使用VUE_NODE来作为运行环境是node的标识 //cli

  • 简单的Vue SSR的示例代码

    前言 最近接手一个老项目,典型的 Vue 组件化前端渲染,后续业务优化可能会朝 SSR 方向走,因此,就先做些技术储备.如果对 Vue SSR 完全不了解,请先阅读官方文档. 思路 Vue 提供了一个官方 Demo,该 Demo 优点是功能大而全,缺点是对新手不友好,容易让人看蒙.因此,今天我们来写一个更加容易上手的 Demo.总共分三步走,循序渐进. 写一个简单的前端渲染 Demo(不包含 Ajax 数据): 将前端渲染改成后端渲染(仍然不包含 Ajax 数据): 在后端渲染的基础上,加上 A

  • vue ssr 实现方式(学习笔记)

    为什么要写本文呢,话说现在vue-ssr 官网上对 vue 服务端渲染的介绍已经很全面了,包括各种服务端渲染框架比如 Nuxt.js . 集成 Koa 和vue-server-renderer 的 node.js 框架 egg.js,都有自己的官网和团队在维护,文档真是面面俱到功能强大,但是,我个人在刚开始看这些资料的时候,总是忍不住发起灵魂三问:"我是谁?我在哪?我在干什么?",提前没有相关知识的人开始学这些,肯定是要走一些弯路或者卡在某个点一段时间的,所以我想把我的学习经验做下总结

  • VUE基于NUXT的SSR 服务端渲染

    Server Side Rendering(服务端渲染) SSR 目的是为了解决单页面应用的 SEO 的问题,对于一般网站影响不大,但是对于论坛类,内容类网站来说是致命的,搜索引擎无法抓取页面相关内容,也就是用户搜不到此网站的相关信息. 原理 将 html 在服务端渲染,合成完整的 html 文件再输出到浏览器. 适用场景 客户端的网络比较慢 客户端运行在老的或者直接没有 JavaScript 引擎上 NUXT 作用就是在 node.js 上进一步封装,然后省去我们搭建服务端环境的步骤,只需要遵

  • vue ssr 指南详读

    本帖说明 该贴是对vue SSR Guide解读和补充,对于官网文档已有内容会以引用方式体现.由于官网demo在国内无法运行,该贴最后也提供了一个完整的可以运行的demo,帖子中提到的代码均是来自于该demo,供学习交流. 介绍 什么是服务器端渲染(SSR)? Vue.js 是构建客户端应用程序的框架.默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM.然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将静态标记"混合"

  • 从0到1构建vueSSR项目之路由的构建

    vue开发依赖的相关配置 Vue SSR 指南 今天先做客户端方面的配置,明天再做服务端的部分. 那么马上开始吧~ 修改部分代码 脚手架生成的代码肯定是不适合我们所用的 所以要修改一部分代码 //App.vue <template> <div id="app"> <router-view></router-view> </div> </template> <script> export default

  • vue/vue-cli+express手把教你搭建SSR

    最近简单的研究了一下SSR,对SSR已经有了一个简单的认知,主要应用于单页面应用,Nuxt是SSR很不错的框架.也有过调研,简单的用了一下,感觉还是很不错.但是还是想知道若不依赖于框架又应该如果处理SSR,研究一下做个笔记. 什么是SSR 把Vue组件渲染为服务器端的HTML字符串,将他们直接发送到浏览器,最后将静态标记混合为客户端上完全交互的应用程序. 为什么要使用SSR 更好的SEO,搜索引擎爬虫爬取工具可以直接查看完全渲染的页面 更宽的内容达到时间(time-to-content),当权请

  • 手把手教你搭建一个vue项目的完整步骤

    目录 一.环境准备 1.安装node.js 2.检查node.js版本 3.为了提高我们的效率,可以使用淘宝的镜像源 二.搭建vue环境 1.全局安装vue-cli 三.创建vue项目 1.用cmd命令创建项目 1.1创建文件 1.2选择配置信息 1.3选择版本 1.4路径模式选择 1.5语法代码格式检查 1.6第三方文件存在的方式 1.7是否保存本次配置信息(保存预设) 1.8创建成功 1.9运行 1.10启动 1.11停止服务 2.用vue资源管理器创建 2.1进入vue资源管理器界面(vu

  • Vue 实现CLI 3.0 + momentjs + lodash打包时优化

    在vue-cli 2.0时代,webpack的配置是有独立文件的,包含在build目录下,修改也比较方便 到vue-cli 3.0后,webpack配置被整合到vue-cli的配置中了,需要配置一些打包插件比较麻烦了,比如优化momentjs压缩包,使用webpack-bundle-analyzer等- 研究后发现vue-cli 3.0使用了chainWebpack来支持额外的插件配置,其实和2.0是差不多的 我们这里使用momentjs,lodash和webpack-bundle-analyz

  • vue 使用 canvas 实现手写电子签名

    功能 兼容 PC 和 Mobile: 画布自适应屏幕大小变化(窗口缩放.屏幕旋转时画布无需重置,自动校正坐标偏移): 自定义画布尺寸(导出图尺寸),画笔粗细.颜色,画布背景色: 支持裁剪 (针对需求:有的签字需要裁剪掉四周空白). 导出图片格式为 base64 : 示例demo 安装 npm install vue-esign --save 使用 main.js 中引入 import vueEsign from 'vue-esign' Vue.use(vueEsign) 页面中使用 必须设置 r

  • 教你搭建按需加载的Vue组件库(小结)

    按需加载的原理 按需加载,本质上是把一个组件库的不同组件 拆分成不同文件 ,按照需要引用对应的文件,而该文件暴露一个 install方法 ,供Vue.use使用. 比如:我只想引用element库里的一个Button组件 import Button from 'element-ui/lib/Button.js' import Button from 'element-ui/lib/theme-chalk/Button.css' Vue.use(Button); 上面的写法比较繁琐,而且需要知道每

  • vue - vue.config.js中devServer配置方式

    前言 这里写你调用接口的基础路径,来解决跨域,如果设置了代理,那你本地开发环境的axios的baseUrl要写为'',即空字符串 只有一个接口ip端口时 devServer: { open: false, // 跨域 proxy: { '/nuojinadm/': { target: 'http://192.168.0.11/', changeOrigin: true } } } 2.设置axios中的baseUrl 与proxy端口一致 baseUrl: { dev: '/nuojinadm/

  • 手把手教你搭建vue3.0项目架构

    前言: GitHub上我开源了vue-cli.vue-cli3两个库,文章末尾会附上GitHub仓库地址.这次把2.0的重新写了一遍,优化了一下.然后按照2.0的功能和代码,按照vue3.0的语法,完全重写了一遍.虽然名字叫cli,其实两个库都是基于vue-cli创建的.做这个的目的是为了工作中快速启动项目,毕竟切片打包.less.axios.vuex.router.UI框架.基础文件目录.权限,这些都是基操,当然项目不同,还是要做些调整的.这两个项目的master分支都是最基础的东西,里面还包

  • node.js express安装及示例网站搭建方法(分享)

    1.首先肯定是要安装Node.JS windows cmd依次输入如下命令: cd C:\Program Files\nodejs\ npm install -g express npm install -g express-generator 2.创建一个示例工程 windows cmd依次输入如下命令: cd C:\Program Files\nodejs\node_global express -e microblog //即ejs,-j(即jade) cd microblog npm i

  • 手把手教你搭建腾讯云服务器入门(图文教程)

    本文由博主 威威喵 原创 博客主页:https://blog.csdn.net/smile_running 背景 暑假期间,愁着无聊但也不能荒废学业吧,毕竟以后想靠技术混口饭吃!为了实施自己的计划,特地挑了一个便宜的云服务器来用作自己的后台:这不是学生狗没钱嘛,所以我就挑了一个腾讯云服务器.虽说配置很低,但够我们玩就行.因为想写一个电商App,数据总不能从本地数据库来吧,那样也太没水平了!因为自己也会一点Java Web 服务器,索性租了一个云服务器,接口啥的自己写,说干就干吧. 由于本人是在校

  • 手把手教你搭建第一个Spring Batch项目的步骤

    一.概述 Spring Batch是一个轻量级,全面的批处理框架. 一个典型的批处理过程可能是: 从数据库,文件或队列中读取大量记录. 以某种方式处理数据. 以修改之后的形式写回数据 Spring Batch 应用架构图: 一个Batch(批处理)过程由一个Job(作业)组成.这个实体封装了整个批处理过程. 一个Job(作业)可以由一个或多个Step(步骤)组成.在大多数情况下,一个步骤将读取数据(通过ItemReader),处理数据(使用ItemProcessor),然后写入数据(通过Item

随机推荐