关闭搜索(ESC)
搜索标签:

如何设计一个vue组件

2016-05-31 浏览:4027 标签: vue 组件 vue-components components

vue现在火的不要不要的,越来越多的公司将vue纳入了技术栈里,我司现在主要把vue用于后端管理系统开发,活动以及app依旧用react。也许不久的将来,vue也会占据一定的地位。毕竟测试摆在这里。点击访问

之前写过文章介绍用vue构建的铜盘系统的细节,但是如果后台系统,一般项目开始,前端布局,后面就得交给javaer自己负责,提供一定的前端支持就好,所以在考虑构建前端架构的时候,不能太过于复杂化,jQuery对于前端,弥久不衰就是因为jQuery简单,易用。那么关键来了,如何用vue构建一套简单的前端布局呢?

在开发了一段时间,总结了一下几点:

1:UI组件化
2:无需打包
3:数据解耦
4:业务代码独立化,不拆分

以上解决方案以后会介绍,现在主要介绍,如何设计一个vue组件。支持commonJS && Globe。

举例实现一个分页插件。

首先创建一个npm包:

mkdir vue-tbjpage
cd vue-tbjpage
npm init

构建项目需要一下npm包:

"devDependencies": {
    "babel-core": "^6.9.0",
    "babel-loader": "^6.2.4",
    "babel-plugin-transform-runtime": "^6.9.0",
    "babel-preset-es2015": "^6.9.0",
    "babel-runtime": "^6.9.0",
    "css-loader": "^0.23.1",
    "es6-promise": "^3.2.1",
    "eslint": "^2.10.2",
    "style-loader": "^0.13.1",
    "stylus": "^0.54.5",
    "stylus-loader": "^2.1.0",
    "vue": "^1.0.24",
    "vue-hot-reload-api": "^1.3.2",
    "vue-html-loader": "^1.2.2",
    "vue-loader": "^8.4.0",
    "vue-style-loader": "^1.0.0",
    "vue-textarea": "^0.1.0",
    "webpack": "^1.13.1"
}

所以在package.json里添加以上代码,执行npm install安装所需模块。

然后我们开始布局文件夹:

vue-tbjpage
---------  components            // 组件
------------------  page.vue
---------  src                   // 源文件
------------------  page-bs.js  // 主要对bootstrap的支持
---------  styles               // 样式文件 
---------  dist                  // 打包生成的资源文件
---------  index.js             // 入口文件
---------  package.json
---------  webpack.config.js    // webpack配置文件
---------  webpack.dev.config.js
---------  readme.md

我们使用webpack对文件进行打包,配置webpack如下:

var vue = require('vue-loader');
var webpack = require('webpack');
var path = require('path');

var paths = {
    src: './src/',
    dist: './dist/'
};

module.exports = {
    entry: {
        'page-bs': paths.src + 'page-bs.js'
    },
    output: {
        path: paths.dist,
        publicPath: paths.dist,
        filename: '[name].js'
    },
    resolve: {
        extensions: ['', '.js', '.vue', '.styl'],
        alias: {
            'src': path.resolve(__dirname, '')
        }
    },
    resolveLoader: {
        root: path.join(__dirname, 'node_modules')
    },
    module: {
        loaders: [{
            test: /\.vue$/,
            loader: 'vue'
        }, {
            test: /\.js$/,
            exclude: /node_modules|vue\/dist|vue-router\/|vue-loader\/|vue-hot-reload-api\//,
            loader: 'babel',
            query: {
                compact: false
            }
        }, {
            test: /\.json$/,
            loader: 'json'
        }, {
            test: /\.(png|jpg|gif)$/,
            loader: 'url',
            query: {
                limit: 10000,
                name: '[name].[ext]?[hash]'
            }
        }, {
            test: /\.styl$/,
            loader: 'style-loader!css-loader!stylus-loader'
        }]
    },
    babel: {
        presets: ['es2015'],
        plugins: ['transform-runtime']
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    ]
};

webpack的配置就无需多说了,RTFM!!!webpack github

现在我们开始写page.vue组件了。

步骤一:构建基础

<style lang="stylus">
</style>
<template>
    <div>
    </div>
</template>
<script>
    export default {
        props: {

        },
        data: {
            return {

            }
        },

        methods: {

        }
    }
</script>

以上是vue components的基础配置,style放置css样式,可以在lang里设置使用哪种css扩展语言。我比较喜欢stylus,也是TK写的。stylus传送门,在template里可以放置DOM,在script放置js代码,这里主要使用ES6语法。

ok,现在我们开始定义父组件与分页组件的数据结构。

在props里设置:

props: {
    pageData: {
        type: Object,
        require: true
    }
}

pageData是一个Object,key主要有一下几个(数据随便写的):

{
    currentPage: 2,
    perPageNum: 20,
    totalPage: 2
}

数据好了,我们假设有以上几个数据,开始写分页HTML代码:

<template>
    <div class="row clearfix">
        <div class="col-xs-7">
            <ul class="pagination clearfix">
                <!-- 左侧切换 -->
                <li :class="{'disabled': pageData.currentPage<=1}">
                    <a href="javascript:void(0)" aria-label="Previous" @click="selectPage(pageData.currentPage-1, pageData.currentPage<=1)">
                        <span aria-hidden="true">«</span>
                    </a>
                </li>

                <!-- 中间 -->
                <li v-for="n in pageShowArray"  :class="{'pageOne': true, 'active': n==pageData.currentPage}">
                    <a href="javascript:void(0)" @click="selectPage(n, n==pageData.currentPage)">{{n}}</a>
                </li>

                <li :class="{'disabled': pageData.currentPage>=pageData.totalPage}">
                    <a href="javascript:void(0)" aria-label="Next" @click="selectPage(pageData.currentPage+1, pageData.currentPage>=pageData.totalPage)">
                        <span aria-hidden="true">»</span>
                    </a>
                </li>
            </ul>
        </div>
        <div class="col-xs-2">
            <div class="input-group clearfix goto-div">
                <input type="text" class="form-control" value="{{goToNum}}" v-model="goToNum">
                <span class="input-group-btn">
                    <button class="btn btn-default" type="button" @click="gotoPage()    ">跳转</button>
                </span>
            </div>
        </div>
        <div class="col-xs-3">
            <div class="pagination-info">当前第{{pageData.currentPage}}页/总共{{pageData.totalPage}},每页{{pageData.perPageNum}}条</div>
        </div>
    </div>
</template>

这里面的模板代码,可以详见vue文档的介绍。看这篇文档的前提是对vue基础知识有一个详细的认识。

虽然基于bootstrap,但是为了美观,稍微写一点css吧。在style里添加如下代码:

<style lang="stylus">
.pagination
    margin 0
.pagination-info
    padding 6px 12px
    border 1px solid #ccc
    border-radius 4px
</style>

在我为了偷懒,跳过了一大堆逻辑问题的前提下,我们差不多写好了一个组件了。现在,我们可以调用了。

import VuePage from '../components/page'

但是,对,问题来了,如果我这样调用,就需要打包,可以有像jQuery一样简单的调用么?

可以啊!!!

我们在/src/page-bs.js里写下:

import VuePage from '../components/page'
window.VuePage = VuePage

对,我们把VuePage给了window。但是你会想,这样还是不可以调用啊,不急,我们还有神器webpack存在。

在webpack.config.js里配置了打包文件:

entry: {
    'page-bs': paths.src + 'page-bs.js'
}

然后我们执行webpack命令,就会在dist下生成一个page-bs.js文件。那么我们怎么用这个文件呢?

直接script调用!!!。如下:

<script src="./dist/page.js"></script>

然后在vue的代码里配置组件:

<script>
new Vue({
    data: {
        pageData: {
            currentPage: 2,
            perPageNum: 20,
            totalPage: 2
        }
    },
    components: {
        'vue-page': VuePage
    }
})
</script>

配置好后,就可以直接在页面使用了:

<vue-page :page-data="pageData"></vue-page>

ok,至此,一个分页组件就写好了。贴上全部代码:

<style lang="stylus">
.pagination
    margin 0
.pagination-info
    padding 6px 12px
    border 1px solid #ccc
    border-radius 4px
</style>
<template>
    <div class="row clearfix">
        <div class="col-xs-7">
            <ul class="pagination clearfix">
                <!-- 左侧切换 -->
                <li :class="{'disabled': pageData.currentPage<=1}">
                    <a href="javascript:void(0)" aria-label="Previous" @click="selectPage(pageData.currentPage-1, pageData.currentPage<=1)">
                        <span aria-hidden="true">«</span>
                    </a>
                </li>

                <!-- 中间 -->
                <li v-for="n in pageShowArray"  :class="{'pageOne': true, 'active': n==pageData.currentPage}">
                    <a href="javascript:void(0)" @click="selectPage(n, n==pageData.currentPage)">{{n}}</a>
                </li>

                <li :class="{'disabled': pageData.currentPage>=pageData.totalPage}">
                    <a href="javascript:void(0)" aria-label="Next" @click="selectPage(pageData.currentPage+1, pageData.currentPage>=pageData.totalPage)">
                        <span aria-hidden="true">»</span>
                    </a>
                </li>
            </ul>
        </div>
        <div class="col-xs-2">
            <div class="input-group clearfix goto-div">
                <input type="text" class="form-control" value="{{goToNum}}" v-model="goToNum">
                <span class="input-group-btn">
                    <button class="btn btn-default" type="button" @click="gotoPage()    ">跳转</button>
                </span>
            </div>
        </div>
        <div class="col-xs-3">
            <div class="pagination-info">当前第{{pageData.currentPage}}页/总共{{pageData.totalPage}},每页{{pageData.perPageNum}}条</div>
        </div>
    </div>
</template>

<script>

    export default {
        props: {
            pageData: {
                type: Object,
                require: true
            }
        },
        ready () {

        },

        created () {
            let self = this

            self.insertPage()

            self.$watch('pageData.currentPage', function(val) {
                self.insertPage()
            })
            self.$watch('pageData.totalPage', function(val) {
                self.insertPage()
            })
        },

        data () {
            return {
                goToNum: 1,
                pageShowArray: []
            }
        },

        methods: {
            insertPage () {
                let self = this
                self.pageShowArray = []
                if(self.pageData.totalPage<=8 || self.pageData.currentPage<=4) {
                    let nowTotalPage = self.pageData.totalPage>8?8:self.pageData.totalPage
                    for(let i=1;i<=nowTotalPage;i++) {
                        self.pageShowArray.push(i);
                    }
                } else {
                    if(self.pageData.currentPage>4&&self.pageData.currentPage<=self.pageData.totalPage-4) {
                        for(let i=self.pageData.currentPage-3;i<=self.pageData.currentPage+4;i++) {
                            self.pageShowArray.push(i);
                        }
                    } else {
                        for(let i=self.pageData.totalPage-7;i<=self.pageData.totalPage;i++) {
                            self.pageShowArray.push(i);
                        }
                    }
                }
            },

            gotoPage () {
                let thisNum = parseInt(this.goToNum)
                if(thisNum > this.pageData.totalPage) {
                    this.badPageSelect(2)
                } else {
                    if(thisNum != this.pageData.currentPage) {
                        this.selectPageByNum(thisNum)
                    }
                }
            },

            selectPage (num, type) {
                if(!type) {
                    let thisNum = parseInt(num)
                    this.selectPageByNum(thisNum)
                }
            },

            badPageSelect (status) {
                this.$dispatch('bad-page-select', status)
            },

            selectPageByNum (num) {
                this.$dispatch('page-back-num', num)
            }
        }
    }

</script>

总结

vue的确是一个神器,解决了以前前端存在的很多问题。而且vue的api设计的也非常简单,只要有点基础的,一个上午就可以看完,当然设计一个简单的组件,一个上午也足足够了。

添加评论