vue+vuex构建单页应用

基本

  • 构建工具:webpack

  • 语言:ES6

  • 分号:行首分号规则(行尾不加分好,[,(,/,+,-开头时在行首加分号)

  • 配套设施:webpack全家桶,vue全家桶

《vue+vuex构建单页应用》

项目结构

  • 基本目录结构

《vue+vuex构建单页应用》

  • api:封装与后端接口交互的操作

  • common:放置一些reset.css之类的

  • components:组件

  • entry:项目入口文件index.js,index.css,index.html

  • filters:过滤器。注:虽然vue2.0已经基本废弃(只保留了对文本的过滤)了Vue.filter的用法,此目录下的方法仍然可用于官方推荐用来替代过滤器的计算属性的计算中

  • mixins:一些通用类的混入部分。比如全选、多选可抽出通用的list-toggle

  • mock:本地开发的mock数据

  • utils:封装的工具,如对上传文件、日期处理等的封装

  • views:单页应用的视图(视图也是组件,也可放到components中,但个人觉得放在这里比较一目了然)

  • vuex:放置store,actions,mutations,state

  • fis-conf.js:用于测试环境联调时fis实时将前端资源推送到开发机上

如果有自定义指令,还可以加上directives目录(vue的几个可扩展的地方都可以单独做一个目录)。对于项目目录,也可以使用官方提供的另一个工具vue-cli来生成,它还会自动构建单元测试(unit)和端对端测试(e2e)的目录和简单示例。

基础组件

vue除了双向绑定外的一个最大特点就是提供了强大的组件树系统,组件化也是 web发展的趋势.
每一个Vue实例就是一个组件,构造一个组件的也很简单:

var myComponent = Vue.extend({
    template: '',
    ...
})
// 全局注册组件,tag 为 my-component
Vue.component('my-component', MyComponent)

更推荐的做法是写成*.vue形式的单文件组件,搭配vue-loader使用(下图来自官方文档)。
《vue+vuex构建单页应用》

更多关于组件的内容,见官方文档组件
另外,在使用单文件组件时,样式会被打包到js中并在运行时会以<style>节点的形式插入到<head>里面。此时如果想将组件的样式打包到输出的css文件中,只需要在webpack.config.jsmodule.exports中加上以下配置即可:

vue: {
    loaders: {
        js: 'babel-loader?presets[]=es2015&plugins[]=transform-runtime&comments=false',
        css:ExtractTextPlugin.extract(['css-loader'])
    }
}

刚开始一个项目时,如果在有自己特定的UI设计风格,可能需要单独封装一些textinput,checkbox,radio等基础组件;如果没有的话(如普通的后台管理系统),也可以使用Google Material Design,已经有对应的实现material-design-lite。并且vue社区中也已经有针对它的vue组件封装vue-mdl

应用骨架

以“xx管理后台”为例,首先分为上(导航)下(主体内容)两部分,基本结构为:

《vue+vuex构建单页应用》

接下来在views里面心间user.vue,作为用户管理模块入口,如果每个模块还需要包含二级导航(通常是在页面左侧部分),user.vue可以像这样:

《vue+vuex构建单页应用》

这两个文件中用到的router-view,都是vue官方路由插件vue-router提供的。
然后是配置单页应用的路由:

《vue+vuex构建单页应用》

在对应的视图组件中,通过route选项的钩子函数,来确定时图在出现和消失的过程中需要执行的行为。更多路由相关,见官方文档

这样,一个基本的:上->[左|右]的单页应用骨架就有了。(其他类型的应用也可依此类推)

应用状态管理

应用组件化之后,就需要解决组件之间的通信问题。针对组件之间的通信问题,vue提供了三种方式: props属性传递,直接通过引用调用组件方法,自定义事件通信,通过v-ref(在vue2.0已简化为ref)来建立子组件索引从而调用子组件方法。

  • porps:基于属性传递,vue提供了单次绑定、单向绑定和双向绑定。(虽然双向绑定在vue2.0 中已经废弃)

  • 通过引用:子组件可以用this.$parent访问它的父组件。根实例的后代可以用this.$root访问它。父组件有一个数组this.$children,包含它所有的子元素。

  • 通过自定义事件通信:每个Vue实例都是一个事件触发器:

    • 使用$on()监听事件

    • 使用$emit()在它上面触发事件

    • 使用$dispatch()派发事件,事件沿着父链冒泡(vue2.0已废弃)

    • 使用$broadcast()广播事件,事件向下传导给所有的后代(vue2.0已废弃)
      vue2.0中,可以单独使用一个Vue实例来来担任eventBus的作用。

除了这几种方式,当应用比较复杂时,官方推荐使用另一个官方插件vuex

类似于reactreduxvuevuexstore也包含一个全局的状态树state;修改state的行为mutations(对应reduxactions);执行修改的动作actions(对应reduxcreateAction)。

《vue+vuex构建单页应用》

以全局alert组件的状态为例:

  • 创建state

export default {
    alert: {
        show: false,
        type: 'alert',
        message: '',
        onSure: null,
        onClose: null
    }
}
  • 创建mutations

export default {
    SHOW_ALERT (state, data) {
        data.show = true
        state.alert = data
    },
    HIDE_ALERT (state) {
        state.alert.show = false
    }
}
  • 创建actions

/*主页面涉及到的actions*/
let noop = () => {}
/*显示浮层alert*/
export const showAlert = ({dispatch}, message = '') => {
    if(!message) {
        return false
    }
    dispatch('SHOW_ALERT', {
        type: 'alert',
        message: message,
        onClose: noop
    })
}
/*显示浮层confirm*/
export const showConfirm = ({dispatch}, data = {}) => {
    if(!data.message) {
        return false
    }
    data.type = 'confirm'
    if(typeof data.onClose != 'function') {
        data.onClose = noop
    }
    if(typeof data.onConfirm != 'function') {
        data.onConfirm = noop
    }
    dispatch('SHOW_ALERT', data)
}
/*隐藏浮层*/
export const hideAlert = ({dispatch}) => dispatch('HIDE_ALERT')
  • 构建store

import Vue from 'vue'
import Vuex from 'vuex'
import actions from './actions'
import mutations from './mutations'
import state from "./state"
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
Vue.config.debug = debug
export default new Vuex.Store({
    state,
    mutations,
    actions,
    strict: debug
})

然后在应用的根组件中,通过以下方式获取vuex的功能:

/*引入vuex*/
import store from "../vuex/store"
let App = Vue.extend({
    store,
    components: {
        'admin-header': adminHeader,
        'alert': alert
    }
})

然后再在自组件中的vuex模块通过以下方式获取状态以及触发状态改变的动作:

《vue+vuex构建单页应用》

应用的数据交互

api层

记得之前看过民工叔叔(徐飞)的某篇文章里说的,数据层能够跟UI层分离,这样即使UI底层库更换了,也可以使用数据层。同理如果想要对api交互进行替换(如想把某些ajax库换成浏览器支持的fetch api),也可以直接在这一层进行更改。

mock数据

在开发阶段,有时需要mock一些数据来测试应用。推荐一个对restful api友好的第三方工具json-server

《vue+vuex构建单页应用》

index.js

var users = require('./database/users')
module.exports = function() {
    return {
        "users": users
    }
}

database/users.js

module.exports = {
    "users": [
        {
            "user_id": "233", 
            "user_name": "哈哈哈", 
        }, 
        {
            "user_id": "233", 
            "user_name": "哈哈哈", 
        }
    ], 
    "more": true, 
    "result": "SUCCESS"
}

然后终端执行json-server mock/index.js -port 9999就开启了一个restful的服务了。(也可以把这句写到npm script中)

接下来还差一步,就是需要用到webpack-dev-serverproxy配置:

《vue+vuex构建单页应用》

这样,所有访问/rest/*的接口都会被代理到json-server的服务上.

应用测试

一个完整的应用应该还具备单元测试、端对端测试等。目前比较成熟的测试框架社区中也有不少,但由于还没油深入研究过,此处不展开。

总结

文章主要是从搭建一款单页应用的整体架构上来进行描述,可能有一些地方不是那么详细,以后有时间在针对单独某些模块再详细描述吧。。
顺便安利一发开源的两个vue组件,欢迎拍砖:

    原文作者:huiyuan
    原文地址: https://segmentfault.com/a/1190000006149788
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞