V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
PanJiaChen
V2EX  ›  前端开发

手摸手,带你用 vue 撸后台 系列一

  •  2
     
  •   PanJiaChen ·
    PanJiaChen · 2017-05-03 14:52:55 +08:00 · 7893 次点击
    这是一个创建于 2768 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目地址

    前言

    说好的教程终于来了,第一篇文章主要来说一说在开始写业务代码前的一些准备工作吧,但这里不会教你 webpack 的基础配置,热更新怎么做,webpack 速度优化等等,有需求的请自行 google。

    目录结构

    ├── build                      // 构建相关  
    ├── config                     // 配置相关
    ├── src                        // 源代码
    │   ├── api                    // 所有请求
    │   ├── assets                 // 主题 字体等静态资源
    │   ├── components             // 全局公用组件
    │   ├── directive              // 全局指令
    │   ├── filtres                // 全局 filter
    │   ├── mock                   // mock 数据
    │   ├── router                 // 路由
    │   ├── store                  // 全局 store 管理
    │   ├── styles                 // 全局样式
    │   ├── utils                  // 全局公用方法
    │   ├── view                   // view
    │   ├── App.vue                // 入口页面
    │   └── main.js                // 入口 加载组件 初始化等
    ├── static                     // 第三方不打包资源
    │   ├── jquery
    │   └── Tinymce                // 富文本
    ├── .babelrc                   // babel-loader 配置
    ├── eslintrc.js                // eslint 配置项
    ├── .gitignore                 // git 忽略项
    ├── favicon.ico                // favicon 图标
    ├── index.html                 // html 模板
    └── package.json               // package.json
    
    

    这里来简单讲一下 src 文件

    api 和 views

    简单截取一下公司后台项目,现在后台大概有三十多个 api 模块 Paste_Image.png 如图可见,模块是很多的,而且随着业务的迭代,模块会越来越多。 所以这里建议根据业务模块来划分 views,并且将 views 和 api 两个模块一一对应,方便维护.如下图 Paste_Image.png

    如 article 模块下放的都是文章相关的 api,这样不管项目怎么累加,api 和 viede 的维护还是清晰的,当然也有一些全区公用的 api 模块,如七牛 upload,remoteSearch 等等,这些单独放置就行。

    components

    这里的 components 放置的都是全局公用的一些组件,如上传组件,富文本等等。一些页面级的组件建议还是放在各自 views 文件下,如图 Paste_Image.png

    store

    这里我个人建议不要为了用 vuex 而用 vuex。就拿我司的后台项目来说,它虽然比较庞大,二三十个业务模块,十几种权限,但业务之间的耦合度是很低的,文章模块和评论模块几乎是俩个独立的东西,所以根本没有必要使用 vuex 来存储 data,每个页面里存放自己的 data 就行。当然有些数据还是需要用 vuex 来统一管理的,如登录 token,用户信息,或者是一些全局个人偏好设置等,还是用 vuex 管理更加的方便,具体当然还是要结合自己的业务场景的。总之还是那句话,不要为了用 vuex 而用 vuex !


    webpack

    这里是用 vue-cli 为基础模板构建的,如果你对这个有什么疑惑请自行 google,相关的配置介绍文章已经很详细了,这里就不再展开了。简单说一说需要注意到地方。

    jquery

    管理后台不同于前台项目,会经常用到一些第三方插件,但有些插件是不得不依赖 jquery 的,如市面上好的富文本基都是依赖 jquery 的,所以干脆就直接引入到项目中省事(gzip 之后只有 34kb,而且常年 from cache,不要考虑那些吹毛求疵的大小问题,这几 kb 和提高的开发效率根本不能比)。但是如果第三方库的代码中出现$.xxx 或 jQuery.xxx 或 window.jQuery 或 window.$则会直接报错。要达到类似的效果,则需要使用 webpack 内置的 ProvidePlugin 插件,配置很简单,只需要

         new webpack.ProvidePlugin({
           $: 'jquery' ,
           'jQuery': 'jquery'
         })
    

    这样当 webpack 碰到 require 的第三方库中出现全局的$、jQeury 和 window.jQuery 时,就会使用 node_module 下 jquery 包 export 出来的东西了。

    alias

    当项目逐渐变大之后,文件与文件直接的引用关系会很复杂,这时候就需要使用 alias 了。 有的人喜欢 alias 指向 src 目录下,再使用相对路径找文件

    resolve: {
        alias: {
            '~': resolve(__dirname, 'src')
        }
    }
    //使用
    import stickTop from '~/components/stickTop'
    

    我习惯于

    alias: {
      'src': path.resolve(__dirname, '../src'),
      'components': path.resolve(__dirname, '../src/components'),
      'api': path.resolve(__dirname, '../src/api'),
      'utils': path.resolve(__dirname, '../src/utils'),
      'store': path.resolve(__dirname, '../src/store'),
      'router': path.resolve(__dirname, '../src/router')
    }
    
    //使用
    import stickTop from 'components/stickTop'
    import getArticle from 'api/article'
    

    没有好与坏对与错,纯看个人喜好和团队规范。


    eslint

    不管是多人合作还是个人项目,代码规范是很重要的。这样做不仅可以很大程度地避免基本语法错误,也保证了代码的可读性。这所谓工欲善其事,必先利其器,个人推荐 eslint+vscode 来写 vue,绝对有种飞一般的感觉。效果如图: eslintGif.gif 每次保存,vscode 就能标红不符合 eslint 规则的地方,同时还会做一些简单的自我修正。安装步骤如下:

    首先安装 eslint 插件 eslint1.png

    安装并配置完成 ESLint 后,我们继续回到 VSCode 进行扩展设置,依次点击 文件 > 首选项 > 设置 打开 VSCode 配置文件,添加如下配置

    
        "files.autoSave":"off",
        "eslint.validate": [
           "javascript",
           "javascriptreact",
           "html",
           { "language": "vue", "autoFix": true }
         ],
         "eslint.options": {
            "plugins": ["html"]
         }
    
    

    这样每次保存的时候就可以根据根目录下.eslintrc.js 你配置的 eslint 规则来检查和做一些简单的 fix。这里提供了一份我平时的 eslint 规则地址,都简单写上了注释。每个人和团队都有自己的代码规范,统一就好了,去打造一份属于自己的 eslint 规则上传到 npm 吧,如饿了么团队的config,vue 的config


    封装 axios

    我们经常遇到一些线上的 bug,但测试环境很难模拟。其实可以通过简单的配置就可以在本地调试线上环境。 这里结合业务封装了 axios,线上代码

    import axios from 'axios';
    import { Message } from 'element-ui';
    import store from '../store';
    import router from '../router';
    
    export default function _fetch(options) {
      return new Promise((resolve, reject) => {
        const instance = axios.create({
          baseURL: process.env.BASE_API, //通过 env 环境变量切换 api 地址
          // timeout: 2000, //请求超时时间
          headers: { 'X-Ivanka-Token': store.getters.token }//后台项目,每个请求都需要 token,判断权限
        });
        instance(options)
                .then(response => {
                  const res = response.data;
                  //我司通过自定义 code 来判断 如:code 不等于 20000 就被试为异常
                  if (res.code !== 20000) {
                    console.log(options); // for debug
                    Message({
                      message: res.message,
                      type: 'error',
                      duration: 5 * 1000
                    });
                    // 50014:Token 过期了 50012:其他客户端登录了 50008:非法的 token
                    if (res.code === 50008 || res.code === 50014 || res.code === 50012) {
                      Message({
                        message: res.message,
                        type: 'error',
                        duration: 5 * 1000
                      });
                      // 登出
                      store.dispatch('FedLogOut').then(() => {
                        router.push({ path: '/login' })
                      });
                    }
                    reject(res);
                  }
                  resolve(res);
                })
                .catch(error => {
                  Message({
                    message: '发生异常错误,请刷新页面重试,或联系程序员',
                    type: 'error',
                    duration: 5 * 1000
                  });
                  console.log(error); // for debug
                  reject(error);
                });
      });
    }
    
    //使用
    export function getInfo(token) {
      return _fetch({
        url: '/user/info',
        method: 'get',
        params: { token }
      });
    }
    

    比如后台项目,每一个请求都是要带 token 来验证权限的,这样封装以下的话我们就不用每个请求都手动来塞 token,或者来做异常处理,一劳永逸。 而且因为我们的 api 是更具 env 环境变量动态切换的,如果以后线上出现了 bug,我们只要将配置 dev.env.js

    module.exports = {
        NODE_ENV: '"development"',
        BASE_API: '"https://api-dev"', //修改为'"https://api-prod"'就行了
        APP_ORIGIN: '"https://wallstreetcn.com"' //为公司打个广告 pc 站为 vue+ssr
    }
    
    

    妈妈再也不用担心我调试线上 bug 了。 当然这里只是简单举了个例子,axios 还可以执行多个并发请求,拦截器什么的,大家自行去研究吧。


    多环境

    vue-cli 默认只提供了 dev 和 prod 两种环境。但其实正真的开发流程可能还会多一个 sit 环境,就是所谓的测试环境。所以我们就要简单的修改一下代码。其实很简单就是设置不同的环境变量

    "build:prod": "NODE_ENV=production node build/build.js",
    "build:sit": "NODE_ENV=sit node build/build.js",
    

    之后在代码里自行判断,想干就干啥

    var env = process.env.NODE_ENV === 'production' ? config.build.prodEnv : config.build.sitEnv
    

    新版的 vue-cli 也内置了 webpack-bundle-analyzer 一个模块分析的东西,相当的好用。使用方法也很简单,和之前一样封装一个 npm script 就可以。

    //package.json
    "build:sit-preview": "NODE_ENV=sit npm_config_preview=true  npm_config_report=true node build/build.js"
    
    //之后通过 process.env.npm_config_report 来判断是否来启用 webpack-bundle-analyzer
    
    var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
    webpackConfig.plugins.push(new BundleAnalyzerPlugin())
    

    效果图 analyzer.png webpack-bundle-analyzer 这个插件还是很有用的,对后期的代码优化什么的,最重要的是它够装逼~


    前后端交互

    每个公司都有自己一套的开发流程,没有绝对的好与坏。这里我来讲讲我司的前后端开发流程。

    跨域问题

    首先前后端交互不可避免的就会遇到跨域问题,我司现在全是用 cors 来解决的,如果你司后端嫌麻烦不肯配置的话,dev 环境也可以通过 webpack-dev-server 的 proxy 来解决,开发环境用 nginx 反代理一下就好了,具体配置这里就不展开了。

    前后端的交流问题

    其实大家也知道,平时的开发中交流成本占据了我们很大一部分时间,但前后端如果有一个好的协作方式的话能解决很多时间。我司开发流程都是前后端和产品讨论项目,之后 后端 mock api 生成好文档,我们前端才是对接接口的。这里推荐一个文档生成器swagger swagger是一个 REST APIs 文档生成工具,可以在许多不同的平台上从代码注释中自动生成,开源,支持大部分语言,社区好,总之就是一个神奇,给大家透露一下我司的 api 文档(swagger 自动生成,ui 忽略)

    Paste_Image.png url 地址,需要传是没参数,需要的传参类型,返回的数据格式什么都一清二楚了,我们大前端终于不用再看后端的脸色了~

    前端自行 mock

    如果后端不肯来帮你 mock 数据的话,前端自己来 mock 也是很简单的。你可以使用 mock server 或者使用mockjs+rap也是很方便的。


    iconfont

    element-ui 默认的 icon 不是很多,这里要安利一波阿里的iconfont简直是神器,不管是公司项目还是个人项目都在使用。它提供了 png,ai,svg 三种格式,同时使用也支持 unicode,font-class,symbol 三种方式。由于是管理后台对兼容性要求不高,楼主平时都喜欢用 symbol,就是用 svg 的方式引入 iconfont.js,之后就能欢快的去选 icon 了,还能自己上传 icon。晒一波我司后台的图标(都是楼主自己发挥的)。 iconfont.png


    router-view

    different router the same component vue。真实的业务场景中,这种情况很多。比如router-view.png 我创建和编辑的页面使用的是同一个 component,默认情况下当这两个页面切换时并不会触发 vue 的 created 或者 mounted 钩子,官方说你可以通过 watch $route 的变化来做处理,但其实说真的还是蛮麻烦的。后来发现其实可以简单的在 router-view 上加上一个唯一的 key,来保证路由切换时都会重新渲染触发钩子了。这样简单的多了。

    <router-view :key="key"></router-view>
    
    computed: {
        key() {
            return this.$route.name !== undefined? this.$route.name + +new Date(): this.$route + +new Date()
        }
     }
    

    优化

    有些人会觉得现在构建是不是有点慢,我司现在技术栈是容器服务,后台项目会把 dist 文件夹里的东西都会打包成一个 docker 镜像,基本步骤为

    npm install
    npm run build:prod
    加打包镜像,一共是耗时如下
    

    Paste_Image.png

    还是属于能接受时间的范围。 主站 PC 站基于 nodejs、Vue 实现服务端渲染,所以不仅需要依赖 nodejs,而且需要利用 pm2 进行 nodejs 生命周期的管理。为了加速线上镜像构建的速度,我们利用 taobao 源 https://registry.npm.taobao.org 进行加速, 并且将一些常见的 npm 依赖打入了基础镜像,避免每次都需要重新下载。 这里注意下 建议不要使用 cnpm install 或者 update 它的包都是一个 link,反正会有各种诡异的 bug,这里建议这样使用

    npm install --registry=https://registry.npm.taobao.org
    

    如果你觉得慢还是有可优化的空间如使用 webpack dll 或者把那些第三方 vendor 单独打包 external 出去,或者我司现在用的是 http2 可以使用 AggressiveSplittingPlugin 等等,这里有需求的可以自行优化。


    占坑

    常规占坑,这里是手摸手,带你用 vue 撸后台 系列一,下一篇会主要讲讲搭建用户系统,二次登录,权限验证这些东西,有时间的话,还会写一下基于 element-ui 动态换肤的实现方案。或者大家可以留言说说想要看一些什么。

    13 条回复    2017-06-13 16:03:18 +08:00
    code4life
        1
    code4life  
       2017-05-03 14:57:16 +08:00
    不错,强烈前排支持!!!
    准备顺着你的路子学下去,楼主一定要及时更新,加油!
    kslr
        2
    kslr  
       2017-05-03 15:03:46 +08:00
    ESLint 有时候会破坏掉代码,所以默认开自动修正不会有问题吗
    Olive
        3
    Olive  
       2017-05-03 15:06:07 +08:00
    手摸手,好可怕
    loading
        4
    loading  
       2017-05-03 15:08:00 +08:00 via Android
    只要是 vuejs 相关的都要支持。
    PanJiaChen
        5
    PanJiaChen  
    OP
       2017-05-03 15:10:09 +08:00
    @kslr 个人习惯了 养成习惯就好。也可以写完一个模块之后统一 npm run lint
    Mbin
        6
    Mbin  
       2017-05-03 15:58:44 +08:00
    楼主写得很不错,赞一个
    HLT
        7
    HLT  
       2017-05-03 16:14:42 +08:00 via iPhone
    凑表脸的 还摸我手
    Tunar
        8
    Tunar  
       2017-05-03 16:28:31 +08:00 via Android
    别摸我,痒~
    forzalianjunting
        9
    forzalianjunting  
       2017-05-03 16:30:48 +08:00
    赞,和我司目前某个项目的后台结构很像,去年刚接手的时候直接就上的 vue+element-ui,开发效率直线上升
    lyseky
        10
    lyseky  
       2017-05-03 23:07:23 +08:00
    前端膜拜一下
    billyu
        11
    billyu  
       2017-05-04 09:33:46 +08:00
    node -v7.7.3
    npm -v4.1.2

    npm install:
    npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules\chokidar\node_modules\fsevents):
    npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

    请问这是啥情况?
    PanJiaChen
        12
    PanJiaChen  
    OP
       2017-05-04 09:56:26 +08:00
    @billyu npm 依赖问题,只是一个 warning 忽略就可以了
    DearTanker
        13
    DearTanker  
       2017-06-13 16:03:18 +08:00
    看了一圈过去,感觉都是针对后台的框架,有没有针对前台的框架可以推荐的?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1013 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 19:37 · PVG 03:37 · LAX 11:37 · JFK 14:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.