使用node+vue.js实现SPA应用


Posted in Javascript onJanuary 28, 2016

业务需求

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

服务端

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

#app.ts 只贴重要代码

var webpack = require('webpack')
var webpackDevMiddleware = require('webpack-dev-middleware')
var WebpackConfig = require('./webpack.config')

import * as index from "./routes/index";
import * as foo from "./routes/foo";
import * as bar from "./routes/bar";

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('/', index.index);
app.get('/foo', foo.index);
app.get('/bar', bar.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的插件

var Vue = new vueServer.renderer(); //创建一个服务端的vue

export function index(req: express.Request, res: express.Response) {

  //创建一个组件
  var vm = new Vue({
    template: `
    <p>This is index!</p>
    `
  });

  //等待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_data = {name:"张三"}'})
  });

};
#layout.ejs 访问这个SPA的所有url返回的都是这个页面 <meta>标签都可以动态设置,只要传参数进来就可以
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Vue Router Example</title>
  <style>
    .v-link-active {
      color: red;
    }
  </style>
  <script>
    //定义一些前端需要用到的全局属性,文章ID或用户信息什么的
    //index.ts中传过来的是 window.cm_data = {name:"张三"}
    //前端就能访问到了
    <%-server_data%>
  </script>
</head>
<body>

//这里的id是前端需要用到的一个标识
<div id="app">
  <h1>Hello App!</h1>
  <p>
    <a v-link="{ path: '/foo' }">Go to Foo</a>
    <a v-link="{ path: '/bar' }">Go to Bar</a>
  </p>
  //router-view是客户端vue-router需要解析的dom
  //server_html是根据访问url地址生成的html,是做SEO的重点,不加载下面的app.js也可以看到内容
  <router-view> <%-server_html%> </router-view>
</div>
//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可以很简单实现懒加载

//懒加载路由 只有访问这个路由才会加载js
import Foo from 'bundle?lazy!../../components/foo' //配合webpack的bundle-loader,轻松实现懒加载
import Bar from 'bundle?lazy!../../components/bar'
import Index from 'bundle?lazy!../../components/index'

var App = Vue.extend({})

Vue.use(VueRouter)

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 //前端路由定义,
  },
  '/foo': {
    component: Foo
  },
  '/bar': {
    component: Bar
  }
})

//启动APP
router.start(App, '#app')

需要完善的地方

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

不放源码都是瞎扯。

源码地址

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

Javascript 相关文章推荐
jquery 锁定弹出层实现代码
Feb 23 Javascript
JavaScript 程序编码规范
Nov 23 Javascript
ie7+背景透明文字不透明超级简单的实现方法
Jan 17 Javascript
js去除浏览器默认底图的方法
Jun 08 Javascript
JavaScript图片轮播代码分享
Jul 31 Javascript
jQuery实现大转盘抽奖活动仿QQ音乐代码分享
Aug 21 Javascript
jquery对象和DOM对象的任意相互转换
Feb 21 Javascript
详解微信小程序 template添加绑定事件
Jun 23 Javascript
基于vue组件实现猜数字游戏
May 28 Javascript
在一个页面实现两个zTree联动的方法
Dec 20 Javascript
layui使用数据表格实现购物车功能
Jul 26 Javascript
JavaScript图像放大镜效果实现方法详解
Jun 28 Javascript
jQuery+css实现的tab切换标签(兼容各浏览器)
Jan 28 #Javascript
javascript实现随机显示星星特效
Jan 28 #Javascript
基于javascript实现全国省市二级联动下拉选择菜单
Jan 28 #Javascript
JS实现动态生成表格并提交表格数据向后端
Nov 25 #Javascript
jQuery+css实现的时钟效果(兼容各浏览器)
Jan 27 #Javascript
jQuery实现的分子运动小球碰撞效果
Jan 27 #Javascript
jQuery+css3实现转动的正方形效果(附demo源码下载)
Jan 27 #Javascript
You might like
php你的验证码安全码?
2007/01/02 PHP
jQuery为iframe的body添加click事件的实现代码
2011/04/07 Javascript
js 事件处理函数间的Event物件是否全等
2011/04/08 Javascript
jQuery-Tools-overlay 使用介绍
2012/07/14 Javascript
基于jquery的图片幻灯展示源码
2012/07/15 Javascript
jquery删除数据记录时的弹出提示效果
2014/05/06 Javascript
解决node-webkit 不支持html5播放mp4视频的方法
2015/03/11 Javascript
CSS+JS实现点击文字弹出定时自动关闭DIV层菜单的方法
2015/05/12 Javascript
js实现登陆遮罩效果的方法
2015/07/28 Javascript
谈谈PHP中相对路径的问题与绝对路径的使用
2016/08/16 Javascript
BootStrap table删除指定行的注意事项(笔记整理)
2017/02/05 Javascript
js中自定义react数据验证组件实例详解
2018/10/19 Javascript
JS中使用new Option()实现时间联动效果
2018/12/10 Javascript
JavaScript中this用法学习笔记
2019/03/17 Javascript
Vue项目使用localStorage+Vuex保存用户登录信息
2019/05/27 Javascript
javascript的delete运算符知识点总结
2019/11/19 Javascript
[55:47]DOTA2上海特级锦标赛C组小组赛#2 LGD VS Newbee第三局
2016/02/27 DOTA
跟老齐学Python之关于类的初步认识
2014/10/11 Python
用Python的Django框架完成视频处理任务的教程
2015/04/02 Python
在Python编程过程中用单元测试法调试代码的介绍
2015/04/02 Python
Django中更新多个对象数据与删除对象的方法
2015/07/17 Python
python实现简易云音乐播放器
2018/01/04 Python
1分钟快速生成用于网页内容提取的xslt
2018/02/23 Python
python如何将两个txt文件内容合并
2019/10/18 Python
python进度条显示之tqmd模块
2020/08/22 Python
加拿大便宜的隐形眼镜商店:Clearly
2016/09/15 全球购物
土耳其国际性时尚购物网站:Modanisa
2018/01/19 全球购物
三陽商会官方网站:Sanyo iStore
2019/05/15 全球购物
绩效专员岗位职责
2013/12/02 职场文书
计算机网络专业求职信
2014/06/05 职场文书
购房协议书范本(无房产证)
2014/10/07 职场文书
放假通知格式
2015/04/14 职场文书
离婚律师函范本
2015/05/27 职场文书
golang slice元素去重操作
2021/04/30 Golang
python在package下继续嵌套一个package
2022/04/14 Python
德生2P3收音机开箱评测
2022/04/30 无线电