# 原则 ## **奉行原则:代码是给人阅读的,机器只是执行程序的** ### 所需技能: 1. 基础三件套: JavaScript、HTML(5)、CSS(3) 2. [Vue](https://cn.vuejs.org/v2/guide/installation.html)、[Vue Router](https://router.vuejs.org/zh/)、[Vuex](https://vuex.vuejs.org/zh/)(是否使用以项目实际情况为准) 3. [Vue CLI](https://cli.vuejs.org/zh/guide/)(了解,亦可不看,学习中可以试着自己用脚手架搭建) 4. 本项目UI组件库:[Element UI ^2.13.0](https://element.eleme.io/#/zh-CN/component/layout) (不用看,直接用,没什么看的) ### 其他 * PS:```Vue```使用之前,可不用了解原理,亦不用了解本项目架构的搭建(后续学习中可以了解),官网教程认真看一遍,边做边学;Vue全家桶(Vue + Vue Router + Vuex)认真了解下。未对```JavaScript```有所了解,不建议直接学习并使用Vue框架或其他前端框架。 * 如果项目中的代码编写与学习时不相同,是因为使用了ES7中的修饰器,引入了依赖```vue-class-component```,请阅读README文件(本文件)的第十条。 * 项目中遇到的问题及问题解决方法,请记录在doc文件夹下的相关文件里(写在本文件下面亦可),形成问题解决库很重要。 ## **开发之前,请仔细阅读doc文件夹下的所有文件(这很重要)** ## [README.md](./README.md) 介绍项目整体原则以及项目启动流程 --- ## doc文档目录介绍 ## [rule.md](./doc/rule.md) 项目中各个模块的原则以及可能引申涉及出来的问题 --- ## [vue.md](./doc/vue.md) 项目中遇到的有关Vue的问题 --- ## [router.md](./doc/router.md) 项目中遇到的有关router的问题 --- ## [git.md](./doc/git.md) 项目中遇到的有关git的问题 --- ## [mock.md](./doc/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的文件中添加一段代码 ```JavaScript 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如何每次打开子组件弹窗都进行初始化 #### 场景 ```html ``` #### 解决方法 ``` :visible.sync 与 v-if同时使用 ``` ### 3. 关于资源预加载 ```preload``` 和资源预读取 ```prefetch``` #### 场景 针对于Vue-cli 3 webpack打包之后的文件,在首次访问时,会对资源进行预读取,如不想进行预读取可进行如下操作: 在webpack配置文件中,移除 ```prefetch``` 插件(和具体项目框架搭建有所差异): ```JavaScript chainWebpack: (config) => { // 移除 prefetch 插件 config.plugins.delete('prefetch'); }, ``` [**参考Vue CLI 文档**](https://cli.vuejs.org/zh/guide/html-and-static-assets.html#prefetch) ### 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/ ``` 2. 增加变量 test 以 ```VUE_APP_ ```开头的变量会被 ```webpack.DefinePlugin``` 静态嵌入到客户端侧的包中 ```VUE_APP_SERVER_MODE=test vue-cli-service build --modern --mode production``` 3. 以本项目为例: .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 文件 ```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 文档**](https://cli.vuejs.org/zh/guide/mode-and-env.html#模式) **说明:** 测试环境 ```NODE_ENV = 'production'``` ,是因为webpack在打包时,会对文件进行压缩;如果 ```NODE_ENV = 'development'``` 时,在打包时不会对文件进行压缩,导致文件体积会特别大。 ### 5. reload 刷新当前页面 #### 场景 在处理列表时,常常有删除一条数据或者新增数据之后需要重新刷新当前页面的需求。 #### 解决问题 1. 用vue-router重新路由到当前页面,页面是不进行刷新的 2. 采用```window.reload()```,或者```router.go(0)```刷新时,整个浏览器进行了重新加载,闪烁,体验不好 #### 解决方法 ```provide / inject``` 组合 作用:允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。 ```html ``` ### 6. 关于菜单在用户退出重新登录,重复出现问题 #### 原因 使用状态管理机Vuex,用户退出登录时,Vuex的存储数据未清理,导致数据重复 #### 解决方法 1. 在Vuex的退出登录时,清空数据存储(如下) ```js [ACCOUNT_LOGOUT]({ commit }, payload) { return accountApi.accountLogout(payload.id).then((res) => { commit(SET_USERINFO, {}); return res; }); }, ``` [Vuex文档](https://vuex.vuejs.org/zh/) 2. 对数据进行加锁处理(详见account下的components里的pageAside.vue文件) ```js // 获取菜单数据,格式化数据,加锁以防多次重复调用,产生错误 getRulesData() { if (this.flag) { this.flag = false; // ...... this.flag = true; } } ``` ### 7. ESLint for循环 (尽量使用ES6语法) 规则不允许一元运算符++和--。 错误示例: ```js var foo = 0; foo++; var bar = 42; bar--; for (let i = 0; i < l; i++) { return; } ``` 正确示例: ```js 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 循环中出现意外的项目。 ```js for (key in foo) { doSomething(key); } ``` 请注意,```foo.hasOwnProperty(key)```在某些情况下,简单检查可能会导致错误; 见 ```no-prototype-builtins``` 。 规则细节 此规则旨在防止使用```for in```循环而不过滤循环中的结果时可能出现的意外行为。因此,当```for in```循环不会用if语句过滤结果时,它会发出警告。 此规则的错误代码示例: ```js /*eslint guard-for-in: "error"*/ for (key in foo) { doSomething(key); } ``` 此规则的正确代码示例: ```js /*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```,由此造成页面无法进行滚动。 ```html ``` #### 解决问题 不采用```el-image```,使用原生```img``` ```html ``` #### 原因: ``` 尚未知 ``` ### 10. 关于ES7中的修饰器(解释一下相关问题,强烈建议学习一下ES6+语法) #### 解释相关疑问 在此之前,先提一下这个依赖 ```vue-class-component``` 是尤大大推出的Vue对typescript支持的装饰器(库) ```vue-class-component``` 强调了几点用法: 1、```methods``` 可以直接声明为类成员方法 2、```computed``` 可以将计算的属性声明为类属性getter / setter: 3、```data``` 数据可以声明为类属性 4、```data```、```render``` 和所有Vue生命周期挂钩也可以直接声明为类成员方法,但不能在实例本身上调用它们。在声明自定义方法时,应避免使用这些保留名称。 请阅读 [Vue Class Component](https://class-component.vuejs.org/) #### 示:1: TypeScript 语法: ```ts 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: ```js // 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(); } } ```