使用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 相关文章推荐
判断目标是否是window,document,和拥有tagName的Element的代码
May 31 Javascript
深入理解JavaScript系列(7) S.O.L.I.D五大原则之开闭原则OCP
Jan 15 Javascript
深入理解JavaScript系列(11) 执行上下文(Execution Contexts)
Jan 15 Javascript
使用jquery读取html5 localstorage的值的方法
Jan 04 Javascript
jQuery 淡出一个图像到另一个图像的实现代码
Jun 12 Javascript
jquery获取tagName再进行判断
May 29 Javascript
基于javascript实现判断移动终端浏览器版本信息
Dec 09 Javascript
JS实现进入页面时渐变背景色的方法
Feb 25 Javascript
JS访问DOM节点方法详解
Nov 29 Javascript
为什么要使用Vuex的介绍
Jan 19 Javascript
Vue2.0 ES6语法降级ES5的操作
Oct 30 Javascript
javascript的setTimeout()使用方法总结
Nov 20 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无法实现多线程的问题
2015/09/25 PHP
PHP实现文件下载【实例分享】
2017/04/28 PHP
PHP时间日期增减操作示例【date strtotime实现加一天、加一月等操作】
2018/12/21 PHP
PHP pthreads v3下的Volatile简介与使用方法示例
2020/02/21 PHP
HTML页面如何象ASP一样接受参数
2007/02/07 Javascript
腾讯UED 漂亮的提示信息效果代码
2011/09/12 Javascript
通过JQuery将DIV的滚动条滚动到指定的位置方便自动定位
2014/05/05 Javascript
JS上传图片前实现图片预览效果的方法
2015/03/02 Javascript
jQuery EasyUI菜单与按钮详解
2016/07/13 Javascript
JS IOS/iPhone的Safari浏览器不兼容Javascript中的Date()问题如何解决
2016/11/11 Javascript
javascript读取文本节点方法小结
2016/12/15 Javascript
JavaScript简单验证表单空值及邮箱格式的方法
2017/01/20 Javascript
JS复制对应id的内容到粘贴板(Ctrl+C效果)
2017/01/23 Javascript
原生js实现打字动画游戏
2017/02/04 Javascript
vue2.0多条件搜索组件使用详解
2020/03/26 Javascript
javascript算法之二叉搜索树的示例代码
2017/09/12 Javascript
JS使用setInterval实现的简单计时器功能示例
2018/04/19 Javascript
angular 未登录状态拦截路由跳转的方法
2018/10/09 Javascript
详解小程序如何避免多次点击,重复触发事件
2019/04/08 Javascript
Vue infinite update loop的问题解决
2019/04/23 Javascript
node.js使用zlib模块进行数据压缩和解压操作示例
2020/02/12 Javascript
vue中使用echarts的示例
2021/01/03 Vue.js
用Python编写一个每天都在系统下新建一个文件夹的脚本
2015/05/04 Python
python针对excel的操作技巧
2018/03/13 Python
python找出完数的方法
2018/11/12 Python
修改Pandas的行或列的名字(重命名)
2019/12/18 Python
Python项目实战之使用Django框架实现支付宝付款功能
2021/02/23 Python
CSS3制作精致的照片墙特效
2016/06/07 HTML / CSS
CSS3实现翘边的阴影效果的代码示例
2016/06/13 HTML / CSS
浅析border-radius如何兼容IE
2016/04/19 HTML / CSS
英国电子产品购物网站:TobyDeals
2018/07/30 全球购物
学校安全生产承诺书
2014/05/23 职场文书
党员违纪检讨书怎么写
2014/11/01 职场文书
转让协议书
2015/01/27 职场文书
2016年政治理论学习心得体会
2016/01/25 职场文书
MySQL连接控制插件介绍
2021/09/25 MySQL