vuejs項目實戰

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指定當前顯示的組件
  • index.html
<!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.js
// 第一步:使用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);

2,定義路由

1、路由原理

  1. 我們定義三套組件:home、list、detail,每個組件實際對應了一個頁面
  2. 並且每個組件對應一個自定義路由,前端的路由是根據hash實現的,所以我們要監聽hash的改變
  3. Hash的改變會觸發hashchange事件,所以我們訂閱該事件,然後解析路由,切換組件
  • index.html
<!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>
  • js/index.js
// 第一步:使用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);

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

  • app.js 服務端
/**
 * 服務器
 */

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);
  • home.json 模擬請求數據
{
    "errno": 0,
    "name":"zhangsan",
    "age":100
}
  • home.html 發送異步請求
<!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>

4,頭部樣式

說明:公用的部分,不要寫在組件中,儘量寫在父組件(vue實例化對象中),這樣組件可以複用。

  • index.html
<!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>
  • js/index.js
// 實例化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)
        }
    }
});

在這裏插入圖片描述

5,分類按鈕: home頁面中的不變數據

1、說明

  1. 分類按鈕的數據是不變的,因此不變的數據,通常直接寫在js中,變的通過異步請求獲取

  2. 直接寫在頁面中的數據,通常稱之爲同步的數據,通過異步請求獲取的數據,通常稱之爲異步數據

  • index.html
<!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.js
// 拓展工具方法
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);

在這裏插入圖片描述

6,廣告模塊:home頁面中通過異步請求獲取數據

1、說明

  1. 這裏的數據是可變的,所以要寫在異步請求中,

  2. 什麼時候請求數據?看組件什麼時候創建完成。所以要看組件生命週期,組件的生命週期

  3. Vue將組件看成是一個有生命的個體,跟人一樣,定義了各個階段,組件的生命週期:組件的創建過程

  4. 組件生命週期鉤子函數:當組件處在某個階段,要執行某個方法,來通知我們,組件進入某個階段,這個方法就是組件生命週期的鉤子函數

  5. 這些方法在組件中直接定義,會按照順序執行,沒有參數,作用域都是組件實例化對象

  6. 注:爲了讓數據屬性具有特性,我們一定要將該數據在綁定的數據(data)中定義出來

  • index.html
<!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.js
// 拓展工具方法
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);

7,效果圖

在這裏插入圖片描述

8,項目模塊化

1、項目模塊化目錄結構

  • 目錄結構說明
Lib: 庫文件
Util:工具方法
Filter:定義過濾器
Router: 定義路由
Vm:存儲所有組件
    Home:home組件以及樣式
    List:list組件以及樣式
    Detail:detail組件以及樣式
    App.js:vue實例化對象
    App.css: app樣式
Bootstrap.js 啓動的文件
Reset.js 默認樣式

在這裏插入圖片描述
2、個文件代碼展示

  • demo/index.heml
<!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/app.js
/**
 * 服務器
 */

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);

static文件夾下文件

  • 1.1 demo/static/filter/filter.js
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
    })

    // 過濾器無需暴露接口,可以直接使用
});
  • 2.1 demo/static/filter/router.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;
});
  • 3.1 demo/static/util/util.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;
});
  • 4.1 demo/static/vm/app.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.2 demo/static/vm/detail/detail.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.3 demo/static/vm/home/home.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.4 demo/static/vm/list/list.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;
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章