1.1 项目说明

  1、技术架构

      vue.js, 模块化,工程化, 移动端

  2、目录部署  

      Css:所有样式文件

      Data:所有异步接口

      Img:所有图片文件

      Js:所有js文件(2.0)

      index.html

  3、结构说明

      1. 一个页面看成是一个组件,所以要创建三个组件

      2. 页面中只能同时显示一个组件,那么如果只有一个容器元素,就可以实现渲染一个了

      3. Vue中定义了一个叫component自定义元素,跟transition一样,有特殊的功能,用来渲染组件

          component是一个万能的组件容器的元素

          Is属性的属性值是谁,就渲染谁

          为了让属性值是动态变量,我们可以使用v-bind指令

      4. 使用component指定当前显示的组件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, width=device-width">
<title>爱创课堂团购网</title>
</head>
<body>
<div id="app">
<!-- 定义组件容器:定义一个万能的组件元素 view是谁就会渲染谁-->
<component v-bind:is="view"></component>
</div> <script src="vue.js"></script>
<script src="index.js"></script>
</body>
</html>

index.html

// 第一步:使用Vue.extend方法,定义三个组件、每个组件实质就是一个页面
var Home = Vue.extend({ // home组件:模拟显示home页面
template:'<h1>Home</h1>'
});
var List = Vue.extend({ // list组件:模拟显示list页面
template:'<h1>List</h1>'
});
var Detail = Vue.extend({ // detail组件:模拟显示detail页面
template:'<h1>Detail</h1>'
}); // 第二步:将组件注册到页面中 Vue.component
Vue.component('Home',Home);
Vue.component('list',List);
Vue.component('detail',Detail); // 创建vue实例化对象
var app = new Vue({
el:'#app',
data:{
view:'home' // 定义默认渲染视图的名称
}
}); // route路由执行的函数,来控制页面显示的组件(home、list、detail)
var route = function () {
var hash = location.hash; // 获取到url后面传入的参数
hash = hash.replace(/#\/?/,''); // url中 '#' 和 '#/' 是无意义的,因此要过滤掉
hash = hash.split('/');
var map = { // 定义了组件的名称才能够渲染
home:true,
list:true,
detail:true
};
if (map[hash[0]]){ // hash[0]在map表中存在才能渲染 hash[0]= home、list、detail
app.view = hash[0] // 将页面设置成url传入的那个组件
}else {
app.view = 'home' // 否则进入默认路由(home这个组件)
}
app.query = hash.slice(1); // 从第二个成员开始表示路由参数了
// http://localhost:63342/vuejsPro/bbb/demo/index.html?_ijt=p7pgchp7pvfueq4e4tem6c0n0r#home/type/2
// app.query = ["type", "2"]
}; // 实现路由:hashchange 当页面hash改变就会触发此事件
window.addEventListener('hashchange',route); // 每次url给不就会触发
route(); // 当页面加载文成也要执行route路由函数 // window.addEventListener('load',route);

index.js

 1.2 定义路由

   1、路由原理

      1. 我们定义三套组件:home、list、detail,每个组件实际对应了一个页面

      2. 并且每个组件对应一个自定义路由,前端的路由是根据hash实现的,所以我们要监听hash的改变

      3. Hash的改变会触发hashchange事件,所以我们订阅该事件,然后解析路由,切换组件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, width=device-width">
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>爱创课堂团购网</title>
</head>
<body>
<div id="app">
<!-- 定义组件容器:定义一个万能的组件元素 view是谁就会渲染谁-->
<component v-bind:is="view"></component>
</div> <script src="js/vue.js"></script>
<script src="js/index.js"></script> </body>
</html>

index.html

// 第一步:使用Vue.extend方法,定义三个组件、每个组件实质就是一个页面
var Home = Vue.extend({ // home组件:模拟显示home页面
template:'<h1>Home</h1>'
});
var List = Vue.extend({ // list组件:模拟显示list页面
template:'<h1>List</h1>'
});
var Detail = Vue.extend({ // detail组件:模拟显示detail页面
template:'<h1>Detail</h1>'
}); // 第二步:将组件注册到页面中 Vue.component
Vue.component('Home',Home);
Vue.component('list',List);
Vue.component('detail',Detail); // 创建vue实例化对象
var app = new Vue({
el:'#app',
data:{
view:'home' // 定义默认渲染视图的名称
}
}); // route路由执行的函数,来控制页面显示的组件(home、list、detail)
var route = function () {
var hash = location.hash; // 获取到url后面传入的参数
hash = hash.replace(/#\/?/,''); // url中 '#' 和 '#/' 是无意义的,因此要过滤掉
hash = hash.split('/');
var map = { // 定义了组件的名称才能够渲染
home:true,
list:true,
detail:true
};
if (map[hash[0]]){ // hash[0]在map表中存在才能渲染 hash[0]= home、list、detail
app.view = hash[0] // 将页面设置成url传入的那个组件
}else {
app.view = 'home' // 否则进入默认路由(home这个组件)
}
app.query = hash.slice(1); // 从第二个成员开始表示路由参数了
// http://localhost:63342/vuejsPro/bbb/demo/index.html?_ijt=p7pgchp7pvfueq4e4tem6c0n0r#home/type/2
// app.query = ["type", "2"]
}; // 实现路由:hashchange 当页面hash改变就会触发此事件
window.addEventListener('hashchange',route); // 每次url给不就会触发
route(); // 当页面加载文成也要执行route路由函数 // window.addEventListener('load',route);

js/index.js

1.3 异步请求

  1、运行node后端服务

      C:\Users\tom>cd C:\Users\tom\PycharmProjects\vuejsPro\bbb\demo\         # 进入app.js文件夹下
      C:\Users\tom\PycharmProjects\vuejsPro\bbb\demo> node app.js               # 运行
          server running at port 3001

/**
* 服务器
*/ var MINE_TYPES = {
'html': 'text/html',
'xml': 'text/xml',
'txt': 'text/plain',
'css': 'text/css',
'js': 'text/javascript',
'json': 'application/json',
'pdf': 'application/pdf',
'swf': 'application/x-shockwave-flash',
'svg': 'image/svg+xml',
'tiff': 'image/tiff',
'png': 'image/png',
'gif': 'image/gif',
'ico': 'image/x-icon',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'wav': 'audio/x-wav',
'wma': 'audio/x-ms-wma',
'wmv': 'video/x-ms-wmv',
'woff2': 'application/font-woff2'
};
var PORT = 3001;
var http = require('http');
var url = require('url');
var fs = require('fs');
var path = require('path');
var root = process.cwd(); var server = http.createServer(function(request, response) {
var pathname = decodeURIComponent(url.parse(request.url).pathname);
var realPath = path.join(root, pathname);
var ext = path.extname(realPath);
if (!ext) {
realPath = path.join(realPath, '/home.html');
ext = '.html'
}
fs.exists(realPath, function(exists) {
if (exists) {
fs.readFile(realPath, 'binary', function(err, file) {
if (!err) {
response.writeHead(200, {
'Content-Type': MINE_TYPES[ext.slice(1)] || 'text/plain'
});
response.write(file, 'binary');
response.end();
} else {
response.writeHead(500, {
'Content-Type': 'text/plain'
});
response.write('ERROR, the reason of error is ' + err.code + '; Error number is ' + err.errno + '.');
response.end();
}
})
} else {
response.writeHead(404, {
'Content-Type': 'text/plain'
});
response.write('This request URL ' + pathname + ' was not found on this server.');
response.end();
}
}); });
server.listen(PORT);
console.log("server running at port " + PORT);

app.js 服务端

{
"errno": 0,
"name":"zhangsan",
"age":100
}

home.json 模拟请求数据

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
</head>
<body>
<h1>home</h1>
<script src="vue.js"></script>
<script>
// 实现异步请求
var Util = {
ajax:function (url, fn) { // url:请求地址;fn:请求回调函数
var xhr = new XMLHttpRequest(); // 初始化xhr
xhr.onreadystatechange = function () { // 监听事件
if (xhr.readyState === 4){ // 判断状态
if (xhr.status === 200 ){ // 判断状态码
fn && fn(JSON.parse( xhr.responseText) ) // 执行函数fn: xhr.responseText 是从服务端获取到的数据
}
}
};
xhr.open('GET', url, true); // 打开链接
xhr.send(null) // null代替发送数据内容
}
};
// 测试
Util.ajax('home.json',function (res) {
console.log(res,3333333333333333333)
});
</script>
</body>
</html>

home.html 发送异步请求

1.4 头部样式

  说明:公用的部分,不要写在组件中,尽量写在父组件(vue实例化对象中),这样组件可以复用。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, width=device-width">
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>爱创课堂团购网</title>
</head>
<body>
<!-- 定义容器元素 -->
<div id="app">
<header class="header">
<div class="title">
<div class="go-back" v-on:click="goBack"><span class="arrow"><span class="arrow green"></span></span></div>
<div class="login">登录</div>
<h1>爱创课堂团购网</h1>
</div>
<div class="search" v-show="showSearch">
<input type="text" placeholder="请输入搜索关键字" v-on:keyup.enter="gotoSearch">
</div>
</header>
</div>
<script type="text/javascript" src="js/vue2.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>

index.html

// 实例化vue
var app = new Vue({
el: '#app', // 绑定数据
data: {
view: 'home', // 存储路由参数
query: [],
search: '', // 是否显示搜索框
showSearch: true
},
methods: { // 定义方法
gotoSearch: function (e) { // 点击enter进行搜索
this.search = e.target.value // 第一种方式,通过组件间通信
},
goBack: function () { // 返回逻辑
history.go(-1)
}
}
});

js/index.js

  

1.5 分类按钮: home页面中的不变数据

   1、说明

      1. 分类按钮的数据是不变的,因此不变的数据,通常直接写在js中,变的通过异步请求获取

      2. 直接写在页面中的数据,通常称之为同步的数据,通过异步请求获取的数据,通常称之为异步数据

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, width=device-width">
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>爱创课堂团购网</title>
</head>
<body>
<!-- 定义容器元素 -->
<div id="app">
<component v-bind:searchquery="search" v-bind:is="view"></component>
</div>
<!-- 定义首页模板 -->
<script type="text/template" id="tpl_home">
<!-- 定义首页组件容器 -->
<section class="home">
<ul class="types clearfix">
<li v-for="item in types">
<a v-bind:href="'#list/type/' + item.id">
<img v-bind:src="'img/icon/' + item.url" alt="">
<p>{{item.title}}</p>
</a>
</li>
</ul>
</section>
</script>
<script type="text/javascript" src="js/vue2.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>

index.html

// 拓展工具方法
var Util = {
/***
* 获取模板的方法
* @id 模板标签元素id
* return 模板内容
**/
tpl: function (id) {
return document.getElementById(id).innerHTML;
},
/**
* 我们要实现异步请求的方法
* @url 请求地址
* @fn 执行的方法
***/
ajax: function (url, fn) { // 创建xhr对象
var xhr = new XMLHttpRequest(); // 订阅事件
xhr.onreadystatechange = function () { // 监听状态
if (xhr.readyState === 4) { // 判断状态
if (xhr.status === 200) { // 执行回调函数
// console.log(xhr)
fn(JSON.parse(xhr.responseText))
}
}
};
xhr.open('GET', url, true); // 打开链接
xhr.send(null) // 发送数据
}
}; // 定义三个组件
var Home = Vue.extend({
template: Util.tpl('tpl_home'),
data: function () { // 定义数据
return { // 返回值才是真正的数据
types: [
{id: 1, title: '美食', url: '01.png'},
{id: 2, title: '电影', url: '02.png'},
{id: 3, title: '酒店', url: '03.png'},
{id: 4, title: '休闲娱乐', url: '04.png'},
{id: 5, title: '外卖', url: '05.png'},
{id: 6, title: 'KTV', url: '06.png'},
{id: 7, title: '周边游', url: '07.png'},
{id: 8, title: '丽人', url: '08.png'},
{id: 9, title: '小吃快餐', url: '09.png'},
{id: 10, title: '火车票', url: '10.png'}
],
}
},
}); // 注册三个组件
Vue.component('home', Home); // 实例化vue
var app = new Vue({
el: '#app', // 绑定数据
data: {
view: 'home', // 存储路由参数
search: '', // 是否显示搜索框
},
}); // 定义路由
function router () {
var hash = location.hash;
hash = hash.replace(/^#\/?/, '');
hash = hash.split('/');
// 定义规则
var map = {
home: true,
};
if (map[hash[0]]) {
app.view = hash[0]
} else {
app.view = 'home'
}
app.query = hash.slice(1);
}
window.addEventListener('hashchange', router);
window.addEventListener('load', router);

index.js

    

1.6 广告模块:home页面中通过异步请求获取数据

   1、说明

      1. 这里的数据是可变的,所以要写在异步请求中,

      2. 什么时候请求数据?看组件什么时候创建完成。所以要看组件生命周期,组件的生命周期

      3. Vue将组件看成是一个有生命的个体,跟人一样,定义了各个阶段,组件的生命周期:组件的创建过程

      4. 组件生命周期钩子函数:当组件处在某个阶段,要执行某个方法,来通知我们,组件进入某个阶段,这个方法就是组件生命周期的钩子函数

      5. 这些方法在组件中直接定义,会按照顺序执行,没有参数,作用域都是组件实例化对象

      6. 注:为了让数据属性具有特性,我们一定要将该数据在绑定的数据(data)中定义出来

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, width=device-width">
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>爱创课堂团购网</title>
</head>
<body>
<!-- 定义容器元素 -->
<div id="app">
<component v-bind:searchquery="search" v-bind:is="view"></component>
</div>
<!-- 定义首页模板 -->
<script type="text/template" id="tpl_home">
<!-- 定义首页组件容器 -->
<section class="home">
<!-- 定义ad广告 -->
<ul class="ad clearfix">
<li v-for="(item, index) in ad">
<a v-bind:href="'#/detail/' + item.id">
<h3>{{item.title}}</h3>
<p>{{item.description}}</p>
<img v-bind:src="'img/ad/' + item.url" alt="">
</a>
</li>
</ul>
</section>
</script>
<script type="text/javascript" src="js/vue2.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>

index.html

// 拓展工具方法
var Util = {
/***
* 获取模板的方法
* @id 模板标签元素id
* return 模板内容
**/
tpl: function (id) {
return document.getElementById(id).innerHTML;
},
/**
* 我们要实现异步请求的方法
* @url 请求地址
* @fn 执行的方法
***/
ajax: function (url, fn) { // 创建xhr对象
var xhr = new XMLHttpRequest(); // 订阅事件
xhr.onreadystatechange = function () { // 监听状态
if (xhr.readyState === 4) { // 判断状态
if (xhr.status === 200) { // 执行回调函数
// console.log(xhr)
fn(JSON.parse(xhr.responseText))
}
}
};
xhr.open('GET', url, true); // 打开链接
xhr.send(null) // 发送数据
}
}; // 定义三个组件
var Home = Vue.extend({
template: Util.tpl('tpl_home'),
data: function () { // 定义数据
return { // 返回值才是真正的数据
ad: [], // 将数据定义出来,否则没有特性
}
},
created: function () {
this.$parent.showSearch = true;
var me = this;
Util.ajax('data/home.json', function (res) { // 请求数据
if (res && res.errno === 0) {
me.ad = res.data.ad; // 存储数据
me.list = res.data.list;
// 2.0版本不建议使用$set更新数据
// me.$set('ad', res.data.ad)
}
})
}
}); // 注册三个组件
Vue.component('home', Home); // 实例化vue
var app = new Vue({
el: '#app', // 绑定数据
data: {
view: 'home', // 存储路由参数
search: '', // 是否显示搜索框
},
}); // 定义路由
function router () {
var hash = location.hash;
hash = hash.replace(/^#\/?/, '');
hash = hash.split('/');
// 定义规则
var map = {
home: true,
};
if (map[hash[0]]) {
app.view = hash[0]
} else {
app.view = 'home'
}
app.query = hash.slice(1);
}
window.addEventListener('hashchange', router);
window.addEventListener('load', router);

index.js

1.7 效果图 

   

    

1.8 项目模块化

   1、项目模块化目录结构

Lib: 库文件
Util:工具方法
Filter:定义过滤器
Router: 定义路由
Vm:存储所有组件
Home:home组件以及样式
List:list组件以及样式
Detail:detail组件以及样式
App.js:vue实例化对象
App.css: app样式
Bootstrap.js 启动的文件
Reset.js 默认样式

目录结构说明

      

   2、个文件代码展示

    1. index.html和app.js

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, width=device-width">
<!--<link rel="stylesheet" type="text/css" href="css/index.css">-->
<title>爱创课堂团购网</title>
</head>
<body>
<!-- 定义容器元素 -->
<div id="app">
<header class="header">
<div class="title">
<div class="go-back" v-on:click="goBack"><span class="arrow"><span class="arrow green"></span></span></div>
<div class="login">登录</div>
<h1>爱创课堂团购网</h1>
</div>
<div class="search" v-show="showSearch">
<input type="text" placeholder="请输入搜索关键字" v-on:keyup.enter="gotoSearch">
</div>
</header>
<!-- 定义组件容器元素 -->
<!-- <home></home>
<list></list>
<detail></detail> -->
<!-- 同时只能渲染一个组件 -->
<component v-bind:searchquery="search" v-bind:is="view"></component>
</div>
<!-- 定义首页模板 -->
<script type="text/template" id="tpl_home">
<!-- 定义首页组件容器 -->
<section class="home">
<ul class="types clearfix">
<li v-for="item in types">
<a v-bind:href="'#list/type/' + item.id">
<img v-bind:src="'img/icon/' + item.url" alt="">
<p>{{item.title}}</p>
</a>
</li>
</ul>
<!-- 定义ad广告 -->
<ul class="ad clearfix">
<li v-for="(item, index) in ad">
<a v-bind:href="'#/detail/' + item.id">
<h3>{{item.title}}</h3>
<p>{{item.description}}</p>
<img v-bind:src="'img/ad/' + item.url" alt="">
</a>
</li>
</ul>
<!-- 定义商品列表 -->
<ul class="list-container">
<li v-for="item in list">
<a v-bind:href="'#detail/' + item.id">
<img v-bind:src="'img/list/' + item.img" alt="">
<div class="content">
<h3>{{item.title}}</h3>
<p>
<span class="price">{{item.price | price}}</span>
<span class="origin-price">{{item.orignPrice | orignPrice}}</span>
<span class="sales">{{item.sales | sales}}</span>
</p>
</div>
</a>
</li>
</ul>
</section>
</script>
<!-- 定义list模板 -->
<script type="text/template" id="tpl_list">
<section class="list">
<ul class="types clearfix">
<li v-for="ickt in types" v-on:click="sortBy(ickt.key)">
<span>{{ickt.value}}</span>
<span class="arrow"></span>
</li>
</ul>
<ul class="list-container">
<!-- <li v-for="item in list | filterBy query"> -->
<!-- <li v-for="item in list | filterBy searchquery"> -->
<li v-for="item in dealList">
<a v-bind:href="'#detail/' + item.id">
<img v-bind:src="'img/list/' + item.img" alt="">
<div class="content">
<h3>{{item.title}}</h3>
<p>
<span class="price">{{item.price | price}}</span>
<span class="origin-price">{{item.orignPrice | orignPrice}}</span>
<span class="sales">{{item.sales | sales}}</span>
</p>
</div>
</a>
</li>
</ul>
<div class="load-more" v-show="others.length" v-on:click="loadMore">
<span>查看剩余{{others.length}}条团购</span>
<span class="arrow">
<span class="arrow white"></span>
</span>
</div>
</section>
</script>
<!-- 定义详情页模板 -->
<script type="text/template" id="tpl_detail">
<section class="product">
<div class="image-part">
<img v-if="data.src" v-bind:src="'img/item/' + data.src" alt="">
<h1>{{data.title}}</h1>
<p>{{data.description}}</p>
</div>
<div class="price-part">
<span class="price">{{data.price}}</span>
<span class="sign">元</span>
<span class="origin">{{data.orignPrice | orignPrice}}</span>
<span class="buy">立即购买</span>
</div>
<ul class="sale-part clearfix">
<li>支持自动退货</li>
<li>支持随时退货</li>
<li>{{data.sales | sales}}</li>
</ul>
<div class="store-part part">
<div class="header">店家信息</div>
<div class="body">
<p>{{data.storeName}}</p>
<p>{{data.storeAddress}}</p>
</div>
<div class="footer">查看{{data.storeNum}}家分店</div>
</div>
<div class="buy-part part">
<div class="header">购买须知</div>
<div class="body">
<ul>
<li>
<h3>有效期</h3>
<p>{{data.validateTime}}</p>
</li>
<li>
<h3>使用时间</h3>
<p>{{data.useTime}}</p>
</li>
<li>
<h3>使用规则</h3>
<p v-for="item in data.rules">{{item}}</p>
</li>
</ul>
</div>
</div>
</section>
</script>
<!-- 引入脚本文件 -->
<!--<script type="text/javascript" src="static/lib/vue2.js"></script>--> <script type="text/javascript" src="static/lib/sea.js"></script>
<script type="text/javascript" src="static/lib/seajs-css.js"></script>
<script type="text/javascript" src="static/lib/seajs-preload.js"></script>
<script>
// 定义配置引入入口文件
seajs.config({
// 配置根目录
base:'/static',
// 预加载
preload:['lib/vue']
});
// 引入入口文件
seajs.use('bootstrap')
</script>
</body>
</html>

demo/index.heml

/**
* 服务器
*/ var MINE_TYPES = {
'html': 'text/html',
'xml': 'text/xml',
'txt': 'text/plain',
'css': 'text/css',
'js': 'text/javascript',
'json': 'application/json',
'pdf': 'application/pdf',
'swf': 'application/x-shockwave-flash',
'svg': 'image/svg+xml',
'tiff': 'image/tiff',
'png': 'image/png',
'gif': 'image/gif',
'ico': 'image/x-icon',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'wav': 'audio/x-wav',
'wma': 'audio/x-ms-wma',
'wmv': 'video/x-ms-wmv',
'woff2': 'application/font-woff2'
};
var PORT = 3001;
var http = require('http');
var url = require('url');
var fs = require('fs');
var path = require('path');
var root = process.cwd(); var server = http.createServer(function(request, response) {
var pathname = decodeURIComponent(url.parse(request.url).pathname);
var realPath = path.join(root, pathname);
var ext = path.extname(realPath);
if (!ext) {
realPath = path.join(realPath, '/index.html');
ext = '.html'
}
fs.exists(realPath, function(exists) {
if (exists) {
fs.readFile(realPath, 'binary', function(err, file) {
if (!err) {
response.writeHead(200, {
'Content-Type': MINE_TYPES[ext.slice(1)] || 'text/plain'
});
response.write(file, 'binary');
response.end();
} else {
response.writeHead(500, {
'Content-Type': 'text/plain'
});
response.write('ERROR, the reason of error is ' + err.code + '; Error number is ' + err.errno + '.');
response.end();
}
})
} else {
response.writeHead(404, {
'Content-Type': 'text/plain'
});
response.write('This request URL ' + pathname + ' was not found on this server.');
response.end();
}
}); });
server.listen(PORT);
console.log("server running at port " + PORT);

demo/app.js

    2. static文件夹下文件

define(function (require, exports, module) {
// 定义过滤器
Vue.filter('price', function (value) {
return value + '元';
})
// 定义门市价过滤器
Vue.filter('orignPrice', function (value) {
return '门市价:' + value + '元'
})
// 定义销量过滤器
Vue.filter('sales', function (value) {
return '销量' + value
}) // 过滤器无需暴露接口,可以直接使用
});

1.1 demo/static/filter/filter.js

define(function (require, exports, module) {
// 引入vue实例化对象
var app = require('vm/app');
// 定义路由
function router () {
// 解析hash就要获取hash
var hash = location.hash;
// 删除#
// hash = hash.slice(1);
// 删除起始的/
// hash = hash.replace(/^\//, '')
hash = hash.replace(/^#\/?/, '');
// 对/进行切割,保留第一部分,就是组件名称,后面的成员就是参数
hash = hash.split('/');
// 定义规则
var map = {
home: true,
list: true,
detail: true
};
// 只有在map中存在的组件,才能渲染
if (map[hash[0]]) {
// 切换组件
app.view = hash[0]
} else {
// 进入默认路由
app.view = 'home'
}
// 我们还可以将参数存储
app.query = hash.slice(1);
}
// 监听路由改变
window.addEventListener('hashchange', router);
// 页面加载没有触发hashchange事件,我们可以手动触发hashchange事件,或者监听load事件
window.addEventListener('load', router) // 暴露接口
module.exports = router;
});

2.1 demo/static/filter/router.js

define(function (require, exports, module) {
// 拓展工具方法
var Util = {
/***
* 获取模板的方法
* @id 模板标签元素id
* return 模板内容
**/
tpl: function (id) {
return document.getElementById(id).innerHTML;
},
/**
* 我们要实现异步请求的方法
* @url 请求地址
* @fn 执行的方法
***/
ajax: function (url, fn) {
// 创建xhr对象
var xhr = new XMLHttpRequest();
// 订阅事件
xhr.onreadystatechange = function () {
// 监听状态
if (xhr.readyState === 4) {
// 判断状态
if (xhr.status === 200) {
// 执行回调函数
// console.log(xhr)
fn(JSON.parse(xhr.responseText))
}
}
}
// 打开链接
xhr.open('GET', url, true);
// 发送数据
xhr.send(null)
}
}
// 暴露接口
module.exports = Util;
});

3.1 demo/static/util/util.js

define(function (require, exports, module) {
// 引入组件
var home = require('vm/home/home');
var list = require('vm/list/list');
var detail = require('vm/detail/detail');
// 引入样式
require('vm/app.css'); // 实例化vue
var app = new Vue({
el: '#app',
// 绑定数据
data: {
view: 'home',
// 存储路由参数
query: [],
search: '',
// 是否显示搜索框
showSearch: true
},
// 定义方法
methods: {
// 点击enter进行搜索
gotoSearch: function (e) {
// 第一种方式,通过组件间通信
this.search = e.target.value
// 第二种方式存储在路由中
// location.hash = '#/list/search/' + e.target.value
// console.log(e.target.value)
},
// 返回逻辑
goBack: function () {
history.go(-1)
}
}
});
// 暴露接口
module.exports = app;
});

4.1 demo/static/vm/app.js

define(function (require, exports, module) {
// 引入样式
require('./detail.css');
// 使用过滤器
require('filter/filter');
// 使用工具
var Util = require('util/util'); // 详情页
var Detail = Vue.extend({
template: Util.tpl('tpl_detail'),
// 定义数据
data: function () {
return {
data: {}
}
},
// 请求数据
created: function () {
// 隐藏搜索框
this.$parent.showSearch = false;
var me = this;
// 获取商品id
var id = me.$parent.query[0];
// console.log(id)
// 请求数据
Util.ajax('data/product.json', function (res) {
if (res && res.errno === 0) {
// 存储数据
me.data = res.data
}
})
}
});
// 注册组件
Vue.component('detail', Detail) module.exports = Detail; });

4.2 demo/static/vm/detail/detail.js

define(function (require, exports, module) {
// 引入样式
require('vm/home/home.css');
// 引入过滤器
require('filter/filter');
// 引入Util模块
var Util = require('util/util'); var Home = Vue.extend({
template: Util.tpl('tpl_home'),
// 定义数据
data: function () {
// 返回值才是真正的数据
return {
types: [
{id: 1, title: '美食', url: '01.png'},
{id: 2, title: '电影', url: '02.png'},
{id: 3, title: '酒店', url: '03.png'},
{id: 4, title: '休闲娱乐', url: '04.png'},
{id: 5, title: '外卖', url: '05.png'},
{id: 6, title: 'KTV', url: '06.png'},
{id: 7, title: '周边游', url: '07.png'},
{id: 8, title: '丽人', url: '08.png'},
{id: 9, title: '小吃快餐', url: '09.png'},
{id: 10, title: '火车票', url: '10.png'}
],
// num: 111
// 将数据定义出来,否则没有特性
ad: [],
list: []
}
},
created: function () {
this.$parent.showSearch = true;
var me = this;
Util.ajax('data/home.json', function (res) {
if (res && res.errno === 0) {
// 存储数据
me.ad = res.data.ad;
console.log(me.ad,111111111111222222222)
me.list = res.data.list;
// 2.0版本不建议使用$set更新数据
// me.$set('ad', res.data.ad)
}
})
}
});
Vue.component('home',Home); // 暴露接口
module.exports = Home; });

4.3 demo/static/vm/home/home.js

define(function (require, exports, module) {
// 引入样式
require('./list.css');
// 引入过滤器
require('filter/filter'); // 引入工具
var Util = require('util/util'); // 列表页
var List = Vue.extend({
template: Util.tpl('tpl_list'),
// 获取属性数据
props: ['searchquery'],
data: function () {
// 返回值才是绑定的数据
return {
types: [
{value: '价格排序', key: 'price'},
{value: '销量排序', key: 'sales'},
{value: '好评排序', key: 'evaluate'},
{value: '优惠排序', key: 'discount'}
],
// 定义存储数据的变量
list: [],
// 剩余的产品
others: [],
// 搜索字段
query: ''
}
},
// 动态数据
computed: {
dealList: function () {
var me = this;
return this.list.filter(function (obj) {
// console.log(arguments)
return obj.title.indexOf(me.searchquery) >= 0;
})
}
},
// 定义方法
methods: {
// 点击加载更多按钮
loadMore: function () {
// 将other中数据传递给list
this.list = this.list.concat(this.others);
// 此时others中应该没有数据了
this.others = [];
},
// 点击排序按钮
sortBy: function (type) {
// 如果字段是优惠,我们要单独处理
if (type === 'discount') {
this.list.sort(function (a, b) {
// 比较原价-现价的插值
// 升序
// return (a.orignPrice - a.price) - (b.orignPrice - b.price)
// 降序
return (b.orignPrice - b.price) - (a.orignPrice - a.price)
})
} else {
// 数组排序
this.list.sort(function (a, b) {
// 升序
// return a[type] - b[type]
// 降序
return b[type] - a[type]
})
}
// console.log(22, type) }
},
// 视图渲染完成执行的方法
created: function () {
this.$parent.showSearch = true;
// console.log(this)
// 用$parent的数据更新query
this.query = this.$parent.query[1]
var me = this;
var url = 'data/list.json?' + this.$parent.query[0] + '=' + this.$parent.query[1]
// 请求数据
Util.ajax(url, function (res) {
// console.log(res)
// 请求成功,存储数据
if (res && res.errno === 0) {
// 默认显示三条
me.list = res.data.slice(0, 3);
me.others = res.data.slice(3)
}
})
}
});
// 注册
Vue.component('list', List);
// 可以暴露接口,也可以不暴露接口
module.exports = List;
});

4.4 demo/static/vm/list/list.js

06:vuejs项目实战的更多相关文章

  1. Spring Boot → 06:项目实战-账单管理系统

    Spring Boot → 06:项目实战-账单管理系统

  2. 前端开发工程师 - 06.Mini项目实战 - 项目简介

    第6章--Mini项目实战 项目简介 Mini项目简介-Ego社区开发 回顾: 页面制作 页面架构 JavaScript程序设计 DOM编程艺术 产品前端架构 实践课Mini项目--Ego: 主题:漫 ...

  3. NET 分布式架构开发项目实战

    .NET 分布式架构开发项目实战 从头到尾,一步一步讲述一个真实的项目实战,关注点主要是架构的思考和实现,以及如何解决平时项目遇到的一些问题. 同时也司公布源代码. 如何构建高性能,稳定SOA应用之- ...

  4. 【SSH项目实战三】脚本密钥的批量分发与执行

    [SSH项目实战]脚本密钥的批量分发与执行 标签(空格分隔): Linux服务搭建-陈思齐 ---本教学笔记是本人学习和工作生涯中的摘记整理而成,此为初稿(尚有诸多不完善之处),为原创作品,允许转载, ...

  5. java设计模式综合项目实战视频教程

    java设计模式综合项目实战视频教程 视频课程目录如下: 第01节课:本课程整体内容介绍:X-gen系统概况,包括:引入.X-gen项目背景.X-gen的HelloWorld第02节课:X-gen整体 ...

  6. 洗礼灵魂,修炼python(82)--全栈项目实战篇(10)—— 信用卡+商城项目(模拟京东淘宝)

    本次项目相当于对python基础做总结,常用语法,数组类型,函数,文本操作等等 本项目在博客园里其他开发者也做过,我是稍作修改来的,大体没变的 项目需求: 信用卡+商城: A.信用卡(类似白条/花呗) ...

  7. python操作三大主流数据库(10)python操作mongodb数据库④mongodb新闻项目实战

    python操作mongodb数据库④mongodb新闻项目实战 参考文档:http://flask-mongoengine.readthedocs.io/en/latest/ 目录: [root@n ...

  8. 【SSH项目实战】脚本密钥的批量分发与执行【转】

    [TOC] 前言 <项目实战>系列为<linux实战教学笔记>第二阶段内容的同步教学配套实战练习,每个项目循序衔接最终将组成<Linux实战教学笔记>第二阶段核心教 ...

  9. ASP.NET Core分布式项目实战

    ASP.NET Core开发者成长路线图 asp.net core 官方文档 https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/ ...

随机推荐

  1. 用于模型选择的AIC与BIC

    一.AIC(Akaike information Criterion)准则 二.BIC(Bayesian information Criterion)准则 参考文献: [1]AIC与BIC区别

  2. histogram 和 bar plot的区别

    最本质的区别是这样的:histogram用来描述的是numerical变量,而bar plot用来描述的是categorical类型的变量.统计学当中关于变量的分类 这可以从它们的图形上面看到: hi ...

  3. 比较两种方式的form请求提交

    [一]浏览器form表单提交 表单提交, 适用于浏览器提交.像常见的pc端的网银支付,用户在商户商城购买商品,支付时商家系统把交易数据通过form表单提交到三方支付网关,然后用户在三方网关页面完成支付 ...

  4. ASP.Net Core 2.2 MVC入门到基本使用系列 (四)(转)

    本教程会对基本的.Net Core 进行一个大概的且不会太深入的讲解, 在您看完本系列之后, 能基本甚至熟练的使用.Net Core进行Web开发, 感受到.Net Core的魅力. 本教程知识点大体 ...

  5. mybatis运行原理

    mybatis运行原理 运行过程中涉及到的类或者接口 Resources(c) :用于加载mybatis核心配置文件 XMLConfigBuilder(c) :用于解析xml文件(核心配置文件) Co ...

  6. Javascript-可莱托指数判断

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  7. laravel orm

    ###多对多关系 多对多关系和之前的关系完全不一样,因为多对多关系可能出现很多冗余数据,用之前自带的表存不下了. 我们定义两个模型:Article 和 Tag,分别表示文章和标签,他们是多对多的关系. ...

  8. Unity shader学习之半兰伯特光照模型

    半兰伯特光照模型,为Valve公司在开发游戏<半条命>时提出的一种技术,用于解决漫反射光无法到达区域无任凭明暗变化,丢失模型细节表现的问题. 其公式如下: Cdiffuse = Cligh ...

  9. python读取大文件

    最近在学习python的过程中接触到了python对文件的读取.python读取文件一般情况是利用open()函数以及read()函数来完成: f = open(filename,'r') f.rea ...

  10. html5-特殊符号的使用

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...