京宜安oa正式-前端

wangsaitao 9634130dbc 修改 1 年之前
doc 9634130dbc 修改 1 年之前
public 9634130dbc 修改 1 年之前
scripts 9634130dbc 修改 1 年之前
src 9634130dbc 修改 1 年之前
.browserslistrc 9634130dbc 修改 1 年之前
.editorconfig 9634130dbc 修改 1 年之前
.env.development 9634130dbc 修改 1 年之前
.env.production 9634130dbc 修改 1 年之前
.env.test 9634130dbc 修改 1 年之前
.eslintrc.js 9634130dbc 修改 1 年之前
.gitignore 9634130dbc 修改 1 年之前
.htaccess 9634130dbc 修改 1 年之前
README.md 9634130dbc 修改 1 年之前
babel.config.js 9634130dbc 修改 1 年之前
jsconfig.json 9634130dbc 修改 1 年之前
package-lock.json 9634130dbc 修改 1 年之前
package.json 9634130dbc 修改 1 年之前
postcss.config.js 9634130dbc 修改 1 年之前
vue.config.js 9634130dbc 修改 1 年之前

README.md

原则

奉行原则:代码是给人阅读的,机器只是执行程序的

所需技能:

  1. 基础三件套: JavaScript、HTML(5)、CSS(3)
  2. VueVue RouterVuex(是否使用以项目实际情况为准)
  3. Vue CLI(了解,亦可不看,学习中可以试着自己用脚手架搭建)
  4. 本项目UI组件库:Element UI ^2.13.0 (不用看,直接用,没什么看的)

其他

  • PS:Vue使用之前,可不用了解原理,亦不用了解本项目架构的搭建(后续学习中可以了解),官网教程认真看一遍,边做边学;Vue全家桶(Vue + Vue Router + Vuex)认真了解下。未对JavaScript有所了解,不建议直接学习并使用Vue框架或其他前端框架。

  • 如果项目中的代码编写与学习时不相同,是因为使用了ES7中的修饰器,引入了依赖vue-class-component,请阅读README文件(本文件)的第十条。

  • 项目中遇到的问题及问题解决方法,请记录在doc文件夹下的相关文件里(写在本文件下面亦可),形成问题解决库很重要。

开发之前,请仔细阅读doc文件夹下的所有文件(这很重要)

README.md

介绍项目整体原则以及项目启动流程


doc文档目录介绍

rule.md

项目中各个模块的原则以及可能引申涉及出来的问题


vue.md

项目中遇到的有关Vue的问题


router.md

项目中遇到的有关router的问题


git.md

项目中遇到的有关git的问题


mock.md

项目中遇到的有关mock的问题


开发基本原则

  1. 隔离,每个独立的功能抽象为独立的子APP(模块);
  2. 统一,保持模式、功能实现和排版格式的一致性;
  3. 简洁,保持模板和样式的简练、减少第三方库的依赖,避免引入复杂;
  4. 清晰,保持清晰的树形结构,保持单向数据流

项目启动

npm install

开发命令(支持HMR)

npm run serve

编译命令

正式环境打包

npm run build

测试环境打包

npm run dev-build

Lint命令

npm run lint

node-sass 被墙解决方法

npm install node-sass --sass-binary-site=http://npm.taobao.org/mirrors/node-sass

vue-cookies

https://github.com/cmp-cc/vue-cookies

代码整体原则

请查看doc文件夹下的rule.md文件(规范)(很重要)

大部分所遇到的问题可在doc里得到解决

QA

1. 关于Element UI中的NavigationDuplicated {_name: "NavigationDuplicated", name: "NavigationDuplicated"}

解决方法

在引用vue-router的文件中添加一段代码

const originalPush = Router.prototype.push;
Router.prototype.push = function push(location) {
    return originalPush.call(this, location).catch(err => err);
};

注:Element UI ^2.13.0版本已修复此bug

2. Vue如何每次打开子组件弹窗都进行初始化

场景

<el-dialog
    v-if="dialogFormVisible"
    title="新增图标"
    width="45%"
    :visible.sync="dialogFormVisible"
    :append-to-body="true"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    :show-close="false"
    center
>

解决方法

:visible.sync 与 v-if同时使用

3. 关于资源预加载 preload 和资源预读取 prefetch

场景

针对于Vue-cli 3

webpack打包之后的文件,在首次访问时,会对资源进行预读取,如不想进行预读取可进行如下操作: 在webpack配置文件中,移除 prefetch 插件(和具体项目框架搭建有所差异):

chainWebpack: (config) => {
    // 移除 prefetch 插件
    config.plugins.delete('prefetch');
},

参考Vue CLI 文档

4. 环境变量及模式

解决问题

发版过程中存在很多的发版环境,例如测试环境、开发环境等,我们需要根据不同的环境做相应的调整。

解决方法

  1. 增加模式 test

vue-cli-service build --mode test 会在 test 模式下加载可能存在的 .env、.env.test 和 .env.test.local 文件然后构建出生产环境应用。

env.test

```
NODE_ENV=production
VUE_APP_API_BASE_URL=http://47.99.189.168:8090/
```
  1. 增加变量 test 以 VUE_APP_开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中 VUE_APP_SERVER_MODE=test vue-cli-service build --modern --mode production

  2. 以本项目为例:

    .env.test 为本地开发环境

    NODE_ENV = 'development'
    VUE_APP_ENV = 'test'
    

    .env.development 为测试环境

    NODE_ENV = 'production'
    VUE_APP_ENV = 'development'
    

    .env.production 为正式环境(线上环境)

    NODE_ENV = 'production'
    VUE_APP_ENV = 'production'
    

    package.json 文件

    "scripts": {
        "serve": "vue-cli-service serve --mode Member",
        "build": "vue-cli-service build --mode production",
        "dev-build": "vue-cli-service build --mode development",
        "lint": "vue-cli-service lint"
    },
    

    参考Vue CLI 文档

    说明: 测试环境 NODE_ENV = 'production' ,是因为webpack在打包时,会对文件进行压缩;如果 NODE_ENV = 'development' 时,在打包时不会对文件进行压缩,导致文件体积会特别大。

5. reload 刷新当前页面

场景

在处理列表时,常常有删除一条数据或者新增数据之后需要重新刷新当前页面的需求。

解决问题

  1. 用vue-router重新路由到当前页面,页面是不进行刷新的

  2. 采用window.reload(),或者router.go(0)刷新时,整个浏览器进行了重新加载,闪烁,体验不好

解决方法

provide / inject 组合

作用:允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。

<template>
    <el-container>
        <el-main>
            <router-view v-if="isRouterAlive" />
        </el-main>
    </el-container>
</template>
<script>
import Vue from 'vue';
import Component from 'vue-class-component';
@Component({
    provide() {
        return {
            reload: this.reload,
        };
    },
})
export default class Main extends Vue {
    isRouterAlive = true;

    reload() {
        this.isRouterAlive = false;
        this.$nextTick(() => {
            this.isRouterAlive = true;
        });
    }
}

在使用reload的页面:

import Vue from 'vue';
import Component from 'vue-class-component';

@Component({
    inject: ['reload'],
})
export default class Child extends Vue {
    handleSwitchOrg() {
        this.reload();
    }
}
</script>

6. 关于菜单在用户退出重新登录,重复出现问题

原因

使用状态管理机Vuex,用户退出登录时,Vuex的存储数据未清理,导致数据重复

解决方法

  1. 在Vuex的退出登录时,清空数据存储(如下) js [ACCOUNT_LOGOUT]({ commit }, payload) { return accountApi.accountLogout(payload.id).then((res) => { commit(SET_USERINFO, {}); return res; }); }, Vuex文档
  2. 对数据进行加锁处理(详见account下的components里的pageAside.vue文件)

    // 获取菜单数据,格式化数据,加锁以防多次重复调用,产生错误
    getRulesData() {
        if (this.flag) {
            this.flag = false;
    
            // ......
    
            this.flag = true;
        }
    }
    

7. ESLint for循环 (尽量使用ES6语法)

规则不允许一元运算符++和--。

错误示例:

var foo = 0;
foo++;

var bar = 42;
bar--;

for (let i = 0; i < l; i++) {
    return;
}

正确示例:

var foo = 0;
foo += 1;

var bar = 42;
bar -= 1;

for (i = 0; i < l; i += 1) {
    return;
}

8. ESLint for ... in

循环遍历对象for in将包含通过原型链继承的属性。此行为可能会导致 for 循环中出现意外的项目。

for (key in foo) {
    doSomething(key);
}

请注意,foo.hasOwnProperty(key)在某些情况下,简单检查可能会导致错误; 见 no-prototype-builtins

规则细节 此规则旨在防止使用for in循环而不过滤循环中的结果时可能出现的意外行为。因此,当for in循环不会用if语句过滤结果时,它会发出警告。

此规则的错误代码示例:

/*eslint guard-for-in: "error"*/

for (key in foo) {
    doSomething(key);
}

此规则的正确代码示例:

/*eslint guard-for-in: "error"*/

for (key in foo) {
    if (Object.prototype.hasOwnProperty.call(foo, key)) {
        doSomething(key);
    }
    if ({}.hasOwnProperty.call(foo, key)) {
        doSomething(key);
    }
}

9. Element UI el-image组件造成页面无法滚动

场景

a 标签里有 el-image 组件进行链接跳转时,会为body添加内联属性:overflow:hidden,由此造成页面无法进行滚动。

<router-link :to="{ name: 'journal.add'}">
    <el-image
        style="width: 50px; height: 50px"
        src="https://nuodastorage.oss-cn-beijing.aliyuncs.com/gyhq/0/common/png/2019/10/30/15724335243123882.png"
    />
</router-link>

解决问题

不采用el-image,使用原生img

<router-link :to="{ name: 'journal.add'}">
    <img
        style="width: 50px; height: 50px"
        src="https://nuodastorage.oss-cn-beijing.aliyuncs.com/gyhq/0/common/png/2019/10/30/15724335243123882.png"
    >
</router-link>

原因:

尚未知

10. 关于ES7中的修饰器(解释一下相关问题,强烈建议学习一下ES6+语法)

解释相关疑问

在此之前,先提一下这个依赖 vue-class-component 是尤大大推出的Vue对typescript支持的装饰器(库)

vue-class-component 强调了几点用法:

1、methods 可以直接声明为类成员方法

2、computed 可以将计算的属性声明为类属性getter / setter:

3、data 数据可以声明为类属性

4、datarender 和所有Vue生命周期挂钩也可以直接声明为类成员方法,但不能在实例本身上调用它们。在声明自定义方法时,应避免使用这些保留名称。

请阅读 Vue Class Component

示:1:

TypeScript 语法:

import Vue from 'vue';
import Component from 'vue-class-component';
 
@Component({
    props: {
        firstName: String,
        lastName: String
    },
    components: {
        'component-a': ComponentA
    }
})
export class XXXX extends Vue {
    firstName: string; // TypeScript 语法
    lastName: string;
     
    //初始data
    middleName = 'middle';
     
    //computed 属性
    get fullName() {
        return this.firstName + this.lastName;
    }
     
    //method
    hello() {
        alert(`Hello ${this.fullName}!`);
    }
     
    //钩子
    mounted() {
        this.hello();
    }
}

示例2:

// XXXX 类名,最好以文件名命名
import Vue from 'vue';
import Component from 'vue-class-component';
 
@Component({
    props: {
        firstName,
        lastName
    },
    components: {
        ComponentA
    }
})
export class XXXX extends Vue {
    //初始data
    middleName = 'middle';
     
    //computed 属性
    get fullName() {
        return this.firstName + this.lastName;
    }
     
    //method
    hello() {
        alert(`Hello ${this.fullName}!`);
    }
     
    //钩子
    mounted() {
        this.hello();
    }
}