1.1.0 • Published 6 years ago

bmfe-mobile-template v1.1.0

Weekly downloads
3
License
ISC
Repository
-
Last release
6 years ago

使用安装

权限不足请使用 sudo

BM init -f mobile 创建名称 版本 cnpm install

功能介绍

移动端底层已经切换至 vnoic,基于 WeUI,vnoic 又在此之上封装了很多,能很快的构建业务

对于 vnoic 服务指令的引入最好在 common 中进行引入,尽量减小包的体积,按需再向整个业务进行引用

目录结构

·
|-- .gitignore          (需要忽略的文件:/node_modules、/bower_components、/dist)
|-- .babelrc            (babel 编译的文件)
|-- .eslintrc           (js 语法检测文件)
|-- config.js           (BM 配置文件)
|-- node_modules        (node 依赖)
|-- html                (对外输出的页面文件)
|-- dist                (对外输出的 js/css 资源文件)
|-- src                 (工程目录)
    |-- css                 (css 文件目录)
    |-- html                (html 文件目录)
    |-- mock                (mock)
    |-- js                  (js 文件目录)
        |-baseLibs          (此文件一般是外部一些大型的资源文件,且这个文件不会走编译)
        |-common            (公共模块具有完整的页面周期的行为,如:登录)
        |-components        (全局公共组件)
        |-config            (公共配置文件:如路由等)
        |-pages             (页面的业务)
        |-service           (发送请求的服务方法)
        |-store             (全局的 store 如登录信息)
        |-utils             (工具类函数)
        |-widget            (全局生命周期插件)
        |-app.vue           (根节点)
        |-main.js           (业务的根节点 js)
        |-vendor.js         (公共的资源文件引入)

修改配置文件

修改文件目录中的src\js\config\index.js

// 独立开发时,mock 服务的路径
export const TESTPATH = '//fe.benmu-health.com'
// 接口拦截到需要跳转登录页面的 code
export const LOGIN_CODE = 1000
// 需要微信认证
export const WXCONFIG = true
// 请求超时时间
export const AJAXTIMEOUT = 20000
// 请求是否会发送本地的请求
export const LOCAL_AJAX = false

export const DEBUG = {
    // 请求打印
    req: false,
    // 响应打印
    res: false,
    //开启vconsole
    vconsole: false,
    // 开启vue debug
    v_debug: true,
    // 开启vue devtools
    v_devtools: true
}

将上面的配置文件,配置成适用于当前项目的参数

启动

BM server // 启动该项目,该项目会自动打开浏览器

规范

页面配置

vue的组件化中,我们还是需要标识页面级别的组件,单页面的应用更需要设置title微信分享参数,页面界别的vue文件中必须含有一个pageInfo

pageInfo: {
    // 当前页面的标题
    title: '急诊地图',
    // 是否展示微信右上角的选项
    showOptionMenu: true,
    // 分享信息
    shareInfo: {
        // 分享需要记录的key
        shareKey: 'WX_JZDT0000002',
        // 分享的信息
        title: '【收藏文】原来急诊也要跑对医院',
        iconUrl: 'https://img.benmu-health.com/wechatV2/img/mapShare.jpg',
        link: 'https://wechat.benmu-health.com/wechatV2/#/hosMap.intro',
        desc: '患者在线可查询医院急诊项目范畴,获得精准救助'
    }
}

ajax 规范

底层使用axios,需要了解API自行了解axios库,需要在service中配置好自己的请求发送参数如

{
    // 接口名称,调用时会用到,注意重复
    name: 'detail',
    // 发送请求类型
    method: 'GET',
    // 接口描述
    desc: '医生个人信息接口',
    // 本地地址
    localPath: '/test/sectionDoctor/detail',
    // 线上地址
    path: '/mobile/wx/individualDoc/queryById',
    // 发送出去的参数
    params: {
        key: value
    }
}

业务中调用方式

this.$service['xxx/detail']({
    key: value
}, {
    // 额外的参数 如:header
    noShowDefaultError: true
}).then((data) => {
    // 已经过滤了 resData.data,可直接使用,无需判断
    // 具体的过滤器在 config/ajax 中
}, () => {
    // 失败的回调,错误已经自动报了,一般无需关心
});

router 命名规范

命名尽量遵循父级路由.当前路由的规则,一般路由路径不能超过三层,需要考虑效率问题

路由配置文件在 config/router 中,路由配置规则尽量遵循父子路由的关系,子路由都配置在父路由的 children 属性中,配置自路由的好处是在打开子路由返回的时候,父级路由的转台都还保存着

订单模块,有搜索条件的页面,搜索结果列表页面,搜索详情页面,构造出来的路由如下:

{
    path: '/order',
    component: Order,
    children: [{
        path: '/search',
        component: Search
    },{
        path: '/list',
        component: List,
        children: [{
            path: '/detail',
            component: Detail
        }]
    }]
}

父路由中模板文件也需要增加对应的自路由组件

<transition name="child">
    <router-view></router-view>
</transition>

路由中的channelfrom参数会自动带到下一个页面,这个是为了每个业务自动记录当前业务的渠道

router 配置中还有有一些默认的 meta 参数

{
    name: '',
    path: '',
    component: ,
    meta: {
        // 页面是否需要登录,如果需要登录且没有获取到用户登录信息就会弹到登录页面,登录成功之后自动返回
        needLogin: true,
        // 当前页面的 pv 统计参数
        pvKey: 'WX_JZDT0000003'
    }
}

下面是业务中新开页面和返回页面的使用:

this.$router.forword({
    // 不要路径,一般都写 name
    name: ''
})

// 默认返回一步
this.$router.back() === history.go(-1)

// 多级返回
this.$router.back({
    // name,返回到堆栈中最近的一个 name 路由
    name: ''
    // length 代表返回的路径长度
    length: ''
})

页面路由切换代码

store 文件夹下命名规范

我们的 store 的命名规范基本上是围绕着 pages 来做的,pages 确定之后,其实 store 就已经确定了。全局的store是放在根目录下的store里的,其他的业务store都是跟着业务走的

使用都是跟着路由的生命周期完成的,这里并不是真正的把store移除,实质上只是在当前的store树上解除了引用关系,下来再次加回来的时候状态都还在。如果想要每次都是新的状态,应该在state的声明的时候返回一个纯函数,每次使用的时候都是新的状态。

beforeRouteEnter(to, from, next) {
    store.install()
    next()
},
beforeRouteLeave(to, from, next) {
    store.uninstall()
    next()
}

actions 命名规范

由于项目中的 store 是合并到一个 store 上,获取 actions 的方式又是通过 mapActions,所以需要做好命名的区分。

一般我们是用,由于有命名空间,在多个模块间共用的actions不能重复:

DO_something	GET_HOSLIST

getter 命名规范

由于项目中的 store 是合并到一个 store 上,获取 getter 的方式又是通过 mapGetters,所以需要做好命名的区分

有了命名空间,一般是不会重复,一般我们是用:

...mapGetters('tab', ['activeTab', 'showMask'])

pages 文件夹下命名规范

我们认为 pages 下的子模块是遵循某种模块划分的,这样会更有利于之后维护代码,所以一般我们在 pages 下建立模块的时候一般是遵循以页面来划分。 那么 pages 下的目录结构其实就是后台功能菜单栏功能目录的一种体现。

比如有一个目录结构是:

医院管理
    医院列表

对应的 page 下的文件目录结构就应该类似如下(我的英语不好,只能下面这样翻译了):

hosManager
	hosList

上面这种这是模块划分映射目录划分的一种方式,我们还可以依照功能去划分,这样划分可能需要有更强的语义性

模块的构成

当划分出一个子模块之后,我们不能简单粗暴的用一个 .vue 文件把所有业务逻辑完成,除非你的模块功能非常单一,其他的情况,我们希望把模块进行划分,由多个子 component 组成,划分的粒度也需要自己掌握,粒度越细越灵活,但也意味着 component 间的交互会变得复杂。

比如我们划分出了三个模块 headerlistfooter,我们的目录结构按照上面的继续写就会是

	hosManager
		hosList
			index.vue
            store
                index.js
                actions.js
                modules
                    header.js
                    list.js
                    footer.js
            components
                header.vue
                list                // 如果业务非常复杂可做一下拆分
                    index.vue		// 参照 vue2 官网说明,这个文件是作为引入其他文件存在
                    index.js	    // js 逻辑文件
                    index.scss		// 样式文件
                    index.html		// html 文件
                footer.vue

hosList/index.vue 仅仅是作为组织文件,将三个子模块引入,并且做好架子的角色,如 html 中的布局,如果 component 间需要事件交互,这个文件也可以充当中介者的角色。

scroll 的使用

iscroll放到统一的数组中,主要是因为每隔几秒需要更新,以防微信修改大字体。并且可以在某些情况下直接禁用当前页面所有的 iscroll,比如弹窗

$config: {
    // 标志当前业务中有 iscorll
    hasScroll: true,
},
methods: {
    // iscroll 刷新
    refresh(){
        this.$scrollRefresh();
    },
    // iscroll 启用
    able(){
        this.$scrollAble();
    },
    // iscroll 禁用
    disable(){
        this.$scrollDisable();
    }
},
mounted () {
    // scroll 使用的时候需要
    this.$config.scrollArr.push(new IScroll(el, {
        preventDefault: false,
    }));
}

beacon 的使用

发送beacon记录打点,发送到hive中,各业务再去找对应的后端做查询,具体文件在utils/beacon.js

$Beacon(key, page(可缺省), {
    // 保留参数,没有特殊需求这些参数不能覆盖 openId  userId  时间戳
    extra_[a-e]: '额外参数'
    // 额外需要记录的数据
    extra_[d-g]: '额外参数'
});

$Beacon('WX_DLZC0000005');
$Beacon('WX_DLZC0000005', '登录页', {
    extra_d: '注册业务'
});
$Beacon('WX_DLZC0000005', {
    extra_d: '注册业务'
});

vue 使用规范

组件交互

兄弟组件

父组件向子组件传递数据:
props

子组件向父组件抛出事件:
vm.$emit('xxx')

父组件用v-on:xxx="func"来接子组件触发的事件和暴露的数据

虽然vue还提供了$ref$parent来让我们访问其他组件的数据和方法,但为了工程的可维护性,让我们的数据变化的追踪变得有规律可循,我们应尽量避免他们的使用

非兄弟组件 有时候非父子关系的组件也需要通信。在简单的场景下,使用一个空的 Vue 实例作为中央事件总线

业务简单:

var bus = new Vue()

// 触发组件 A 中的事件
bus.$emit('id-selected', 1)

// 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
// ...
})

业务复杂:

请直接使用Vuex
actions中只做异步和分发
commit应该按state结构来细分 尽量避免一个commit修改多个state

格式

如果不给代码做格式化就是等于代码没有写。请原谅我是 tab = 4空格档,就必须是这样的

其他还没有想好,支持一切新的属性,你可以尽情的使用

数组

向数组增加元素时使用 Array#push 来替代直接赋值。

  var someStack = [];
    // bad
    someStack[someStack.length] = 'abracadabra';
    // good
    someStack.push('abracadabra');

ps:尽量不要频繁取值。

当你需要拷贝数组时,使用 Array#slice

    var item = [1,2,3];
    var item_len = item.legth;
    var itemArr = [];
    var i = '';
    // bad
    for(i = 0; i < item_len; i++){
        itemArr[i] = item[i];
    }
    // good
    itemArr = item.slice();

使用 Array#slice 将类数组对象转换成数组

    function trigger() {
      var args = Array.prototype.slice.call(arguments);
      ...
}

字符串

使用单引号,包引字符串

var str = 'helloe world!';

超过 100 个字符的字符串应该使用连接符写成多行。

var str = '<p>你好</p>'+
        '<p>你好<p>'+
        '<p>你好</p>';

ps:尽量用“+”,不用“\”,若字符很长,需折行,不用折行,会影响性能读取问题。

程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下

    for (i = 0; i < length; i++) {
        //bad
        items += '<li>' + messages[i].message + '</li>';
        //good
        items[i] = '<li>' + messages[i].message + '</li>';
    }

    //bad
        return '<ul>' + items + '</ul>';
    //good
        return '<ul>' + items.join('') + '</ul>';

ps:for循环,length提前取出。

{
    "rules": {
        //官方文档 http://eslint.org/docs/rules/
        //参数:0 关闭,1 警告,2 错误

        // "quotes": [0, "single"],                  //建议使用单引号
        // "no-inner-declarations": [0, "both"],     //不建议在{}代码块内部声明变量或函数
        "no-extra-boolean-cast": 1, //多余的感叹号转布尔型
        "no-extra-semi": 1, //多余的分号
        "no-extra-parens": 0, //多余的括号
        "no-empty": 1, //空代码块
        "no-use-before-define": [0, "nofunc"], //使用前未定义
        "complexity": [1, 10], //圈复杂度大于10 警告

        //常见错误
        "comma-dangle": [1, "never"], //定义数组或对象最后多余的逗号
        "no-debugger": 1, //debugger 调试代码未删除
        "no-console": 0, //console 未删除
        "no-constant-condition": 2, //常量作为条件
        "no-dupe-args": 2, //参数重复
        "no-dupe-keys": 2, //对象属性重复
        "no-duplicate-case": 2, //case重复
        "no-empty-character-class": 2, //正则无法匹配任何值
        "no-invalid-regexp": 2, //无效的正则
        "no-func-assign": 2, //函数被赋值
        "valid-typeof": 1, //无效的类型判断
        "no-unreachable": 2, //不可能执行到的代码
        "no-unexpected-multiline": 2, //行尾缺少分号可能导致一些意外情况
        "no-sparse-arrays": 1, //数组中多出逗号
        "no-shadow-restricted-names": 2, //关键词与命名冲突
        "no-undef": 0, //变量未定义
        "no-unused-vars": 1, //变量定义后未使用
        "no-cond-assign": 2, //条件语句中禁止赋值操作
        "no-native-reassign": 2, //禁止覆盖原生对象
        "no-mixed-spaces-and-tabs": 0,

        //代码风格优化
        "no-irregular-whitespace": 0,
        "no-else-return": 0, //在else代码块中return,else是多余的
        "no-multi-spaces": 0, //不允许多个空格
        "key-spacing": [0, {
            "beforeColon": false,
            "afterColon": true
        }], //object直接量建议写法 : 后一个空格前面不留空格
        "block-scoped-var": 1, //变量应在外部上下文中声明,不应在{}代码块中
        "consistent-return": 1, //函数返回值可能是不同类型
        "accessor-pairs": 1, //object getter/setter方法需要成对出现
        "dot-location": [1, "property"], //换行调用对象方法  点操作符应写在行首
        "no-lone-blocks": 1, //多余的{}嵌套
        "no-empty-label": 1, //无用的标记
        "no-extend-native": 1, //禁止扩展原生对象
        "no-floating-decimal": 1, //浮点型需要写全 禁止.1 或 2.写法
        "no-loop-func": 1, //禁止在循环体中定义函数
        "no-new-func": 1, //禁止new Function(...) 写法
        "no-self-compare": 1, //不允与自己比较作为条件
        "no-sequences": 1, //禁止可能导致结果不明确的逗号操作符
        "no-throw-literal": 1, //禁止抛出一个直接量 应是Error对象
        "no-return-assign": [1, "always"], //不允return时有赋值操作
        "no-redeclare": [1, {
            "builtinGlobals": true
        }], //不允许重复声明
        "no-unused-expressions": [0, {
            "allowShortCircuit": true,
            "allowTernary": true
        }], //不执行的表达式
        "no-useless-call": 1, //无意义的函数call或apply
        "no-useless-concat": 1, //无意义的string concat
        "no-void": 1, //禁用void
        "no-with": 1, //禁用with
        "space-infix-ops": 0, //操作符前后空格
        "valid-jsdoc": [0, {
            "requireParamDescription": true,
            "requireReturnDescription": true
        }], //jsdoc
        "no-warning-comments": [1, {
            "terms": ["todo", "fixme", "any other term"],
            "location": "anywhere"
        }], //标记未写注释
        "curly": 0 //if、else、while、for代码块用{}包围
    },
    "env": {
        "es6": true,
        "node": true,
        "browser": true,
        "jquery": true
    },
    "parser": "babel-eslint",
    "ecmaFeatures": {
        "jsx": true
    },
    "plugins": [
        //"react",//写react安装该插件
        "eslint-plugin-html"
    ]
}
1.1.0

6 years ago

1.0.9

7 years ago

1.0.8

7 years ago

1.0.7

7 years ago

1.0.6

7 years ago

1.0.5

7 years ago

1.0.4

7 years ago

1.0.3

7 years ago

1.0.2

7 years ago

1.0.1

7 years ago

1.0.0

7 years ago