xiaonq

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

 

分类:

技术点:

相关文章: