02_09.Vue组件化

1. 首先将文件夹进行分类
组件化
2. 分析
  • 由上面的图可以看出,我们先将功能进行模块化划分,将公共部分提取出来,放到一个common的文件夹内,然后根据不同的页面进行创建不同的文件夹,详细如上图所示。
3. 源代码展示
  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="style/main.css">
</head>
<body>
    <div id="app">
        <app-goods></app-goods>
    </div>

    <!-- vue的全局组件, 全局自定义指令, 全局过滤器必须在vue实例之前定义, 否则vue实例无法解决 -->
    <script src="./lib/vue.js"></script>

    <!-- 公共组件 -->
    <script src="./components/common/header.js"></script>
    <script src="./components/common/footer.js"></script>
    <script src="./components/common/list.js"></script>
    <script src="./components/common/search.js"></script>

    <!-- 页面级别组件 -->
    <script src="./components/goods/goods.js"></script>
    <script src="./components/home/home.js"></script>
    <script src="./components/login/login.js"></script>

    <!-- 导入过滤器 -->
    <script src="./filter/date.js"></script>

    <!-- 导入自定义指令 -->
    <script src="./directive/focus.js"></script>

    <!-- 入口 -->
    <script src="./main.js"></script>
</body>
</html>
  • style/main.css
.wrapper {
    width: 800px;
    margin: 20px auto;
}
.operation {
    margin-bottom: 10px;
    text-align: center;
    line-height: 20px;
    font-size: 18px;
}
.operation input {
    padding: 5px;
    border: 1px solid deepskyblue;
}
.operation button {
    border-radius: 3px;
    background-color: deepskyblue;
}
.search {
    text-align: left;
    line-height: 20px;
    font-size: 18px;
}
.search input {
    padding: 5px;
    border: 1px solid deeppink;
}
#tb{
    width: 800px;
    border-collapse: collapse;
    margin: 20px auto;
}
#tb th{
    background-color: #0094ff;
    color:white;
    font-size: 16px;
    padding: 5px;
    text-align: center;
    border: 1px solid black;
}
#tb td{
    padding: 5px;
    text-align: center;
    border: 1px solid black;
}
  • ./components/common/header.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:27:25 
 * @Last Modified by:   Robyn 
 * @Last Modified time: 2017-11-12 19:27:25 
 */

Vue.component('app-header', {
    template: `
    <header>
        <h1>{{ title }}</h1>
        <p>{{ content }}</p>
    </header>`,
    props: ['title'],
    data: function() {
        return {
            content: '这是头部文件内容'
        };
    }
});
  • ./components/common/footer.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:28:12 
 * @Last Modified by: Robyn
 * @Last Modified time: 2017-11-12 19:28:32
 */

Vue.component('app-footer', {
    template: `
    <footer>
        <p>{{ content }}</p>
        <address>{{ address }}</address>
    </footer>`,
    data: function() {
        return {
            content: '这里是尾部内容',
            address: '北京市'
        };
    }
});
  • ./components/common/list.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:29:30 
 * @Last Modified by:   Robyn 
 * @Last Modified time: 2017-11-12 19:29:30 
 */

Vue.component('app-list', {
    template: `
    <table id="tb">
        <tr>
            <th>编号</th>
            <th>名称</th>
            <th>创建时间</th>
            <th>操作</th>
        </tr>
        <!-- 没有数据才显示, 有数据隐藏 -->
        <tr v-if="list.length == 0">
            <td colspan="4">列表无数据</td>
        </tr>
        <!-- 渲染商品列表 -->
        <tr v-for="item in list" >
            <td>{{ item.id }}</td>
            <td>{{ item.name }}</td>
            <td>{{ item.ctime | date }}</td>
            <td>
                <!-- @符号是v-on的简写方式 -->
                <a href="#" @click="deleteBtn(item.id)">删除</a>
            </td>
        </tr>
    </table>`,

    props: ['list'],

    data: function() {
        return {};
    },

    methods: {
        // 子组件里面监听删除按钮的点击事件, 但是不负责删除操作, 
        // 而是当收到点击事件的时候, 告诉父亲, 它要杀要刮随便
        deleteBtn(id) {
            this.$emit('del', id);
        }
    }
});
  • ./components/common/search.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:30:12 
 * @Last Modified by:   Robyn 
 * @Last Modified time: 2017-11-12 19:30:12 
 */


Vue.component('app-search', {
    template: `
    <div class="search">
        <input type="text" placeHolder="请输入筛选内容" v-model="searchKey">
    </div>`,
    data: function() {
        return {
            searchKey: ''
        };
    },
    watch: {
        // 当这个值变量时, 我通过自定义事件通知父, 同时把最新的值也给传过去
        searchKey() {
            this.$emit('change', this.searchKey);
        }
    }
});
  • ./components/goods/goods.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:31:03 
 * @Last Modified by:   Robyn 
 * @Last Modified time: 2017-11-12 19:31:03 
 */

Vue.component('app-goods', {
    template: `
    <article>
        <app-header v-bind:title="'商品管理'"></app-header>
        <app-search v-on:change="search"></app-search>
        <app-list v-bind:list="searchGoodsList" v-on:del="deleteGoods"></app-list>
        <app-footer></app-footer>
    </article>
    `,
    data: function() {
        return {
            goodsList: [
                { id: 1, name: '法拉利', ctime: new Date() },
                { id: 2, name: '玛莎拉蒂', ctime: new Date() },
                { id: 3, name: '兰博基尼', ctime: new Date() },
                { id: 4, name: '火箭', ctime: new Date() }
            ],
            searchKeyword: ''
        };
    },
    methods: {
        // 通过id删除商品
        deleteGoods(delId) {
            this.goodsList = this.goodsList.filter(
                goods => goods.id != delId
            );
        },
        // 接收搜索子组件传递过来的新值
        search(keyword) {
            this.searchKeyword = keyword;
        }
    },
    computed: {
        // 搜索后的商品列表
        searchGoodsList() {
            return this.goodsList.filter(
                goods => goods.name === this.searchKeyword || this.searchKeyword == ''
            );
        }
    }
});
  • ./components/home/home.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:31:33 
 * @Last Modified by:   Robyn 
 * @Last Modified time: 2017-11-12 19:31:33 
 */

Vue.component('app-home', {
    template: `
    <article>
        <app-header v-bind:title="'首页'"></app-header>
        <app-footer></app-footer>
    </article>
    `,
    data: function() {
        return {};
    }
});
  • ./components/login/login.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:32:08 
 * @Last Modified by:   Robyn 
 * @Last Modified time: 2017-11-12 19:32:08 
 */

Vue.component('app-login', {
    // 每个组件的模版, 必须要使用一个根元素包裹起来
    template: `
    <article>
        <app-header v-bind:title="'登陆'"></app-header>
        <app-footer></app-footer>
    </article>
    `,
    data: function() {
        return {};
    }
});
  • ./filter/date.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:32:43 
 * @Last Modified by:   Robyn 
 * @Last Modified time: 2017-11-12 19:32:43 
 */

// 实现一个处理日期的过滤器
Vue.filter('date', function(time) {
    var date = new Date(time);
    return `${ date.getFullYear() }-${ date.getMonth() + 1 }-${ date.getDate() }`;
});
  • ./directive/focus.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:33:19 
 * @Last Modified by:   Robyn 
 * @Last Modified time: 2017-11-12 19:33:19 
 */

// 实现一个全局自动焦点指令
Vue.directive('focus', {
    inserted(node) {
        node.focus();
    }
});
  • ./main.js
/*
 * @Author: Robyn 
 * @Date: 2017-11-12 19:33:49 
 * @Last Modified by:   Robyn 
 * @Last Modified time: 2017-11-12 19:33:49 
 */

new Vue({
    el: '#app',
    data: {}
});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容