业务需求

最近公司要求开发web版的app,由于app是偏向内容方面,而且带了一个聊天模块,所以一般的多页开发不是很适合,而且主要是手机浏览,对加载速度或者用户体验来说都比较苛刻。调研了很多框架和模式,最后自己东拼西凑搞出来了这么一个玩意。

UI框架 :淘宝SUI

服务端

毫无疑问使用node,使用typescript可以有效的在编码同时查错,强类型语言写服务端毫无压力。


#app.ts 只贴重要代码var webpack = require('webpack')
var webpackDevMiddleware = require('webpack-dev-middleware')
var WebpackConfig = require('./webpack.config')import * as index from "./server/routes/index";
import * as cookbook from "./server/routes/cookbook";
import * as cookbookDetail from './server/routes/cookbookDetail'var app = express();//启动服务的时候 打包并监听客户端用到的文件,webpackDevMiddleware是开发模式,他会打包js在内存里面,你改了文件,它也会重新打包
app.use(webpackDevMiddleware(webpack(WebpackConfig), {publicPath: '/__build__/',stats: {colors: true}
}));//一般的配置项
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.set('view options', { layout: false });
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(methodOverride());
app.use(express.static(__dirname + '/public'));var env = process.env.NODE_ENV || 'development';
if (env === 'development') {app.use(errorHandler());
}//路由配置
app.get('/cookbook', index.index);
app.get('/cookbook/:id', cookbook.index);
app.get('/cookbookDetail/:id', cookbookDetail.index);app.listen(3000, function(){console.log("Demo Express server listening on port %d in %s mode", 3000, app.settings.env);
});export var App = app;

服务端渲染页面

#index.ts
import express = require("express")
import vueServer = require("vue-server") //服务端渲染vue的插件
import request = require('request'); //第3方http请求的插件
import queryString = require('querystring'); //转换get参数的插件var Vue = new vueServer.renderer(); //创建一个服务端的vueexport function index(req: express.Request, res: express.Response) {let vm:vueServer,b:Object,options:Object;options = {method: 'GET',//随便用了一个免费的API,是查询菜谱的url: 'http://apis.baidu.com/tngou/cook/classify?'+queryString.stringify({id : 0,}),headers: {//百度API的开放接口凭证'apikey': 'a369f43a6392605426433831e10765ec'}};request(options,function(err,resp,body){if (!err && resp.statusCode == 200) {b = JSON.parse(body);vm = new Vue({replace : false,template : `<div><!-- 标题栏 --><header class="bar bar-nav"><a class="icon icon-me pull-left open-panel"></a><h1 class="title">{{title}}</h1></header><!-- 这里是页面内容区 --><div class="content"><div class="list-block"><ul><li class="item-content" v-for="item in cookbookClasses"><div class="item-media"><i class="icon icon-f7"></i></div><div class="item-inner"><div class="item-title">{{item.title}}</div></div></li></ul></div></div></div>`,data : {title : '菜谱首页',cookbookClasses: b.tngou,}});}//等待html渲染完成,再返回给浏览器 vueServer.htmlReady是vue-server的自带事件vm.$on('vueServer.htmlReady', function(html:string) {//这里用的是ejs模板 可以把需要用到的数据设置成window下的全局变量,方便客户端的js访问。res.render('layout',{server_html:html,server_data:`window.cm_cookbookClasses = {title : '菜谱首页',cookbookClasses: ${JSON.stringify(b.tngou)}}`})});});
}
#layout.ejs  访问这个SPA的所有url返回的都是这个页面 <meta>标签都可以动态设置,只要传参数进来就可以
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,initial-scale=1.0,user-scalable=no"><title>Vue Router Example</title><link rel="stylesheet" href="//g.alicdn.com/msui/sm/0.6.2/css/??sm.min.css,sm-extend.min.css"><style type="text/css">//注释掉的是vue-router切换的动画, 通过transition,transition-mode设置/*.test-transition {*//*transition: all .5s ease;*//*}*//*.test-enter, .test-leave {*//*opacity: 0;*//*transform: translate3d(10px, 0, 0);*//*}*/h2{font-size: 1rem;}p{font-size: .8rem;}img{max-width: 100%;}</style><script>//定义一些前端需要用到的全局属性,文章ID或用户信息什么的//index.ts中传过来的是 window.cm_data = {name:"张三"}//前端就能访问到了<%-server_data%></script>
</head>
//这里的id是前端需要用到的一个标识
<body id="app">
<div class="page-group"><div class="page page-current">//router-view是客户端vue-router需要解析的dom//server_html是根据访问url地址生成的html,是做SEO的重点,不加载下面的js也可以看到内容<router-view transition="test" transition-mode="out-in"><%-server_html%></router-view></div>
</div>//这里用到了淘宝团队的UI框架SUI,各种组件都有。它依赖zepto
<script type='text/javascript' src='//g.alicdn.com/sj/lib/zepto/zepto.min.js' charset='utf-8'></script>
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/??sm.min.js,sm-extend.min.js' charset='utf-8'></script>//webpack打包好的js,主要是路由配置
<script src="/__build__/app.js"></script>
</body>
</html>

客户端

#app.js 这个是/__build__/app.js,可以用es6编写,webpack会转换的import Vue from './vue.min' //客户端的vue.js
import VueRouter from './vue-router.min' //vue的路由插件,配合webpack可以很简单实现懒加载
import VueResource from './lib/vue-resource.min'    //懒加载路由 只有访问这个路由才会加载jsimport Index from 'bundle?lazy!./routes/index' //配合webpack的bundle-loader,轻松实现懒加载
import Cookbook from 'bundle?lazy!./routes/Cookbook'
import CookbookDetail from 'bundle?lazy!./routes/cookbookDetail'var App = Vue.extend({})Vue.use(VueResource)
Vue.use(VueRouter)//百度API需要用到的参数
Vue.http.headers.common['apikey'] = 'a369f43a6392605426433831e10765ec';var router = new VueRouter({//这里要好好说一下,一定要设置html5模式,不然前后端URL不统一会发生问题//比如访问 http://localhost:3000/ 服务端定义是访问index.ts这个路由文件//如果不是html5模式的话,经过客户端js运行之后会变成http://localhost:3000/#!///在比如直接浏览器输入 http://localhost:3000/foo 服务端定义是访问.ts这个路由文件//如果不是html5模式的话,经过客户端js运行之后会变成 http://localhost:3000/foo/#!///设置了html5模式后,加载完js后不会加上#!这2个类似锚点的字符,实现前后端路由统一如果用户刷新浏览器的话,服务端也能渲染出相应的页面。history: true, //html5模式 去掉锚点 saveScrollPosition: true //记住页面的滚动位置 html5模式适用,实际使用下来没用
})//定义路由,要和服务端路由路径定义的一样
router.map({'/'   : {component: Index //前端路由定义,},'/cookbook/:id': {component: Cookbook},'/cookbookDetail/:id': {component: CookbookDetail}
})
router.redirect({'*': '/cookbook'
})
//启动APP
router.start(App, '#app')

客户端路由

#index.js  这里的模板和服务端的差不多,就增加了@click操作
'use strict';
import Vue from '../lib/vue.min'let Index = Vue.extend({//replace : false, //必须注释掉 不然动画失效template : `<div><!-- 标题栏 --><header class="bar bar-nav"><a class="icon icon-me pull-left open-panel"></a><h1 class="title">{{title}}</h1></header><!-- 这里是页面内容区 --><div class="content"><div class="list-block"><ul><li class="item-content" v-for="item in cookbookClasses" @click="goCookbook(item.id)"><div class="item-media"><i class="icon icon-f7"></i></div><div class="item-inner"><div class="item-title">{{item.title}}</div></div></li></ul></div></div></div>`,data : ()=>{return {title : '菜谱首页',cookbookClasses : []}},methods: {goCookbook(id){//vue-router 路由跳转this.$router.go('/cookbook/'+id);}},//vue-router的属性,可以设置路由的生命周期,具体请查文档route : {//应该是在渲染DOM之前获取数据data : function(transition) {//如果是服务端渲染的,应该设置全局变量,那么客户端就不用异步请求数据了if(window.cm_cookbookClasses){this.$data = window.cm_cookbookClasses;transition.next();}else{let qa_id = 0;//使用vue-resource来获取数据var resource = this.$resource('http://apis.baidu.com/tngou/cook/classify');$.showPreloader(); //这个是显示SUI的加载遮罩层resource.get({id: qa_id}).then((response)=>{$.hidePreloader();if(response.status == 200){this.$data = {title : '菜谱首页',cookbookClasses : response.data.tngou}transition.next();}else{transition.abort();}});}},canActivate: function(){},// 激活状态 把上一次记录的数据,获取出来,需要deactivate状态配合。activate: function (transition) {this.$data = window.cm_cookbookClasses;transition.next()},// 禁用状态 记录这一次的数据,方便以后再进入激活状态可以不用访问网络请求数据deactivate: function (transition) {window.cm_cookbookClasses = this.$data;transition.next()}}
})export default Index

需要完善的地方

  1. 前后端统一模板,已经找到方法了把html分离出来,node端用fs.readFileSync方法获取,客户端用webpack的raw-loader获取html内容

  2. 安卓微信浏览器 vue-resource 设置了headers的apikey,但请求的时候没有带上,导致获取不到数据。

  3. IOS safari浏览器 渲染页面有问题,渲染20条数据,只显示10条左右,监听不到SUI无限滚动到底部的事件

不放源码都是瞎扯。

源码地址 最新的是分支V0.0.1 是个简单的菜谱DEMO

https://github.com/yjj5855/node-vue-server-webpack

使用node+vue.js实现SPA应用,解决了SPA应用的最大缺点SEO相关推荐

  1. js处理请求最多的服务器,vue.js 请求服务器

    理解vue ssr原理,自己搭建简单的ssr框架 先附上demo地址:https://github.com/wmui/vue-s... 第一步:编写entry-client.js和entry-serv ...

  2. vue.js 由浅至深学习宝典

    一. 资源教程 http://www.vue-js.com/topic/56cfc64965cf1c422e006834 http://www.vue-js.com/中文社区 https://lara ...

  3. 用vscode实现vue.js项目的一个完整过程

    1.新建项目 打开Visual studio code 打开一个你想要创建项目的文件夹 打开集成终端:查看 –> 集成终端 或者直接按 ctrl+` 如果没有安装vue-cli,在终端输入: n ...

  4. vue 3.0和2.0区别_一文看懂 Vue.js 3.0 的优化

    Vue.js 从 1.x 到 2.0 版本,最大的升级就是引入了虚拟 DOM 的概念,它为后续做服务端渲染以及跨端框架 Weex 提供了基础. Vue.js 2.x 发展了很久,现在周边的生态设施都已 ...

  5. npm安装vue,在vue/dist目录下没有产生vue.js文件

    遇到问题:在进行npm install时,vue/dist目录下没有生成vue.js的情况. 解决办法:只在vue后面加上@2指定版本即可. npm init -y npm install vue@2 ...

  6. Vue.js ( 比JQuery更加精简的前端框架)

    Vue.js简介 什么是  Vue 什么是 Vue? 考虑这个需求: 我们要把一个 json对象的数据,显示到一个元素上去. 如果不使用 Vue, 那么就会用到 JS 或者 JQuery,通过操作 H ...

  7. 基于 Vue.js 2.0 酷炫自适应背景视频登录页面的设计

    本文讲述如何实现拥有酷炫背景视频的登录页面,浏览器窗口随意拉伸,背景视频及前景登录组件均能完美适配,背景视频可始终铺满窗口,前景组件始终居中,视频的内容始终得到最大限度的保留,可以得到最好的视觉效果. ...

  8. nuxt解决首屏加载慢问题_Nuxt.js超过Vue.js:你什么时候应该使用它,为什么

    Nuxt.js是一个建立在Vue.js上的前端框架,它提供了很好的开发特性,比如服务器端渲染.自动生成路由.改进的元标签管理和SEO改进. Nuxt.js 我们的前端团队并没有真正考虑在客户端使用 N ...

  9. 预渲染vue.js应用程序(使用node或laravel)

    服务器端渲染现在非常流行.但它也并非没有缺点.预渲染是一种替代方法,在某些情况下甚至可能更好.下面我们来看一下如何预渲染vue.js应用程序. 在本文中,我们将探讨预渲染如何与vue.js一起工作,并 ...

最新文章

  1. 客户端动态调用WCF服务中的方法
  2. python爬虫今日头条_python爬虫—分析Ajax请求对json文件爬取今日头条街拍美图
  3. 牛客 - A Simple Game(尼姆博弈变形)
  4. How to get user parameter settings
  5. vim 常用快捷键总结
  6. c语言缓冲池管理算法,操作系统复习资料
  7. Linux系统编程:简单实现ls -R 功能
  8. C#开发ActiveX控件及制作CAB包
  9. 【股价预测】基于matlab BP神经网络股票价格预测【含Matlab源码 345期】
  10. 什么是MXF文件?将MXF转为MP4格式的方法
  11. 两台计算机如何打印机共享打印机,多台电脑怎样共享一台打印机?方法就是这么简单!...
  12. 有关汽车仪表的LED与LCD识别
  13. 开发环境 -- 在linux中写一个脚本拷机
  14. 【服务器数据恢复】华为OceanStor服务器热备盘同步数据失败的数据恢复案例
  15. vue 传参 微信_小猿圈web前端之微信小程序页面间跳转传参方式总结
  16. 高通常用缩写 --不错
  17. Linux ubuntu gtest googletest download compile apply TEST TEST_F TEST_P
  18. 逆向---02.je jmp jnz 、OD调试
  19. 本地使用JSTL标签的方式及问题解决
  20. virtio技术(3)virtqueue机制

热门文章

  1. 墨菲安全受邀与腾讯安全共话软件供应链安全治理
  2. 数字语音信号处理学习笔记——语音信号的数字模型(1)
  3. 30 张图带你了解 CPU 制作全过程
  4. 计算机学哪个编程好,达内机器人编程VS计算机编程,学哪个最好
  5. HTML5吃豆豆游戏开发实战(二)主角移动和动画循环设置
  6. 计算机与应用数学专业就业方向,2020数学与应用数学专业就业前景和就业方向分析...
  7. PostgreSQL之如何敲开PG的大门?
  8. 股票数据接口-陈科肇
  9. 数字图像处理技术及实用 MATLAB实验报告及PS作业
  10. poco c++感性认识