Skip to content

项目架构与优化

项目架构@vue/cli 4.5.7

package.json基本命令配置

js
"scripts": {
  "dev": "vue-cli-service serve", // 开发
  "build": "vue-cli-service build", // 打包
  "lint": "vue-cli-service lint", // 检查 .eslintrc.js 文件配置项
  "report": "vue-cli-service build --report" // 打包并生成分析报告
}

参考文章

一、ESlint + .editorconfig + .gitattributes 统一编程规范

二、scss全局mixins常用css变量、布局

css
/* mixin.scss */
@mixin child-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

/* variable.scss */
$f-color:#000;
$f-size: 16px;
$safe-color: #00FF99;

三、抽离基本库(如:Vue、VueRouter、Vuex、axios、第三方UI库),部署CDN访问

js
// vue.config.js
const cdn = require('./cdn.config.js')
// ...
pages: {
  index: {
    entry: 'src/main.js',
    title: 'vue-cli-framework',
    cdn // cdn注入方式1 
  }
},
chainWebpack: (config) => {
  // 生产&开发 环境忽略的打包文件,如需判断,使用 process.env.NODE_ENV
  // *格式: '引入的包名': 'cdn引入的全局变量名称'
  // *key:value请严格核对,否则影响站点加载全局变量导致白屏
  config.externals({
    'vue': 'Vue',
    'vuex': 'Vuex',
    'vue-router': 'VueRouter',
    'axios': 'axios',
    'element-ui': 'ELEMENT',
  })
},
chainWebpack: (config) => {
  // cdn注入方式 2
  // config
  //   .plugin('html-index')
  //   .tap((args) => {
  //     args[0].cdn = cdn
  //     return args
  //   })
}
html
// public/index.html
<head>
  <title><%= htmlWebpackPlugin.options.title %></title>
  <!-- 不能使用相对路径,否则无法访问到favicon.ico或者loading.css -->
  <link rel="icon" href="<%= BASE_URL %>favicon.ico">
  <!-- CDN注入css -->
  <% for (var i of htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
    <link rel="stylesheet" href="<%=[i] %>" preload>
  <% } %>
  <style>
    /* 居中loading */
    .temp-loading {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
  </style>
</head>
<body>
  <div id="app">
    <img src="./svg/loading.svg" class="temp-loading" alt="加载中,请稍后..." title="加载中,请稍后...">
  </div>
  <!-- CDN注入, ejs模板语法 -->
   <% for (var i of htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
    <script src="<%=[i] %>" preload></script>
    <% } %>
    <img src="./test.jpg" alt="">
</body>

注意

  1. 配置cdn访问后,禁止Element配置按需加载,否则会将Element打包

  2. Element需引入css与js

四、因浏览器请求并发限制,建议适当禁用小模块懒加载(合并打包),实现一次请求

js
// 小模块使用相同 webpackChunkName
const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import(/* webpackChunkName: "Home" */ '../views/Home.vue')
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "Home" */ '../views/about.vue')
  }
]

五、执行RESTful API标准开发, axios封装约定API状态码

参见 阮一峰restful API设计指南

request封装示例

六、Nginx启用Gzip(兼容所有http(s)) + Brotli(兼容https(IE不支持))压缩,前端负责构建压缩文件,减轻服务器开销

  • Gzip开启较为容易(速度提升80%)
  • Brotli开启有点麻烦(暂未研究,较Gzip提升20%)

前端构建配置

  • vue add vue-cli-plugin-compression
js
// vue add 添加的插件,插件可通过 `options.pluginOptions.key` 访问这些选项
pluginOptions: {
  // 具备默认配置,可不配置
  compression: {
    // br压缩
    brotli: {
      filename: '[path].br[query]', // path: 文件完整路径 query: 文件query
      algorithm: 'brotliCompress', // 压缩算法
      include: /\.(js|css|html|svg|json)(\?.*)?$/i, // 匹配文件
      threshold: 1024 * 150, // 单位字节:只压缩150kb的资源
      deleteOriginalAssets: false // 删除源文件
    },
    // gzip压缩
    gzip: {
      filename: '[path].gz[query]',
      algorithm: 'gzip',
      include: /\.(js|css|html|svg|json)(\?.*)?$/i,
      threshold: 1024 * 150,
      deleteOriginalAssets: false
    }
  }
}

TIP

  • Gzip默认会压缩svg,与图片压缩loader可能会存在冲突,因此择一处理svg即可。

七、图片压缩

  • cnpm i -D vue-cli-plugin-compression
js

  // 默认配置的插件配置更改, 查看插件名称:vue inspect --plugins
  chainWebpack: (config) => {
    config.module.rule('images')
      .test(/\.(png|jpe?g|gif)(\?.*)?$/) // svg 放到svg-sprite-loader处理
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        bypassOnDebug: true
      })
  },

注意

  • 如果:Cannot find module 'imagemin-gifsicle', 使用cnpm装该依赖

八、开启http2

js
// nginx轻松配置
server {
  listen  443 ssl http2; // listen中声明即可
}

九、集群/Docker部署 + Nginx负载均衡 + Jenkins持续集成,实现前端无感知发布【后端支持】

svg精灵图

见项目

路由权限控制

参考该仓库设计

其他

动画性能优化

  1. 在 CSS 中,transforms 和 opacity 这两个属性更改不会触发重排与重绘,它们是可以由合成器(composite)单独处理的属性。