使用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 相关文章推荐
javascript第一课
Feb 27 Javascript
JavaScript的变量作用域深入理解
Oct 25 Javascript
Jquery加载时从后台读取数据绑定到dropdownList实例
Jun 09 Javascript
js中小数转换整数的方法
Jan 26 Javascript
Javascript 按位与赋值运算符 (&amp;=)使用介绍
Feb 04 Javascript
JS实现类似百叶窗下拉菜单效果
Dec 30 Javascript
easyui 中的datagrid跨页勾选问题的实现方法
Jan 18 Javascript
原生js实现密码输入框值的显示隐藏
Jul 17 Javascript
在vscode中统一vue编码风格的方法
Feb 22 Javascript
微信小程序单选radio及多选checkbox按钮用法示例
Apr 30 Javascript
vue-cli3项目展示本地Markdown文件的方法
Jun 07 Javascript
vue 更改连接后台的api示例
Nov 11 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
一个分页的论坛
2006/10/09 PHP
PHP四舍五入精确小数位及取整
2014/01/14 PHP
php中url函数介绍及使用示例
2014/02/13 PHP
PHP获取一段文本显示点阵宽度和高度的方法
2015/03/12 PHP
简单介绍win7下搭建apache+php+mysql开发环境
2015/08/06 PHP
PHP简单的MVC框架实现方法
2015/12/01 PHP
laravel框架 api自定义全局异常处理方法
2019/10/11 PHP
javascript 单选框,多选框美化代码
2008/08/01 Javascript
js 兼容多浏览器的回车和鼠标焦点事件代码(IE6/7/8,firefox,chrome)
2010/04/14 Javascript
关于页面嵌入swf覆盖div层的问题的解决方法
2014/02/11 Javascript
js触发onchange事件的方法说明
2014/03/08 Javascript
js中精确计算加法和减法示例
2014/03/28 Javascript
JavaScript验证电子邮箱的函数
2014/08/22 Javascript
Javascript基础知识(一)核心基础语法与事件模型
2014/09/29 Javascript
javascript显式类型转换实例分析
2015/04/25 Javascript
js判断手机访问或者PC的几个例子(常用于手机跳转)
2015/12/15 Javascript
jQuery实现的自动加载页面功能示例
2016/09/04 Javascript
JQuery统计input和textarea文字输入数量(代码分享)
2016/12/29 Javascript
JS图片压缩(pc端和移动端都适用)
2017/01/12 Javascript
原生JS实现左右箭头选择日期实例代码
2017/03/14 Javascript
前端html中jQuery实现对文本的搜索功能并把搜索相关内容显示出来
2017/11/14 jQuery
微信小程序实现tab和swiper切换结合效果
2020/07/17 Javascript
微信小程序tabBar模板用法实例分析【附demo源码下载】
2017/11/28 Javascript
JavaScript中严格判断NaN的方法
2018/02/16 Javascript
vue 表单输入格式化中文输入法异常问题
2018/05/30 Javascript
layui问题之模拟select点击事件的实例讲解
2018/08/15 Javascript
[05:08]2014DOTA2国际邀请赛 Hao专访复仇的胜利很爽
2014/07/15 DOTA
pandas的排序和排名的具体使用
2019/07/31 Python
利用Pytorch实现简单的线性回归算法
2020/01/15 Python
Python DataFrame使用drop_duplicates()函数去重(保留重复值,取重复值)
2020/07/20 Python
N.Peal官网:来自伦敦的高档羊绒品牌
2018/10/29 全球购物
党员一句话承诺大全
2014/03/28 职场文书
公司转让协议书
2016/03/19 职场文书
Vue2.0搭建脚手架
2022/03/13 Vue.js
Python中time标准库的使用教程
2022/04/13 Python
Android开发EditText禁止输入监听及InputFilter字符过滤
2022/06/10 Java/Android