组织版前端开发心得分享①

这是一篇面向公司内部的分享,部分方案具有时效性

SCSS 变量

使用 SCSS 变量主要解决的问题:

我们可以把常用的颜色统一用语义化变量来替代,此处我们定义为 variable.scss 文件。

// variable.scss

// 定义变量
$color-default: #285cd4;
$color-success: #34a89f;
$color-error: #e06565;
$color-warning: #eacb4a;
$color-sub-text: #808695;
$color-border: #e8eaec;
$box-shadow: -1px 0px 7px 2px rgba(0, 0, 0, 0.03);

// 定义混入
@mixin multi-ellipsis($line: 2) {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: $line;
  overflow: hidden;
  word-break: break-all;
}

// app.css
@import './variables.scss';

// 使用变量、混入
.some-css {
	border: 1px solid $color-border;
  @include multi-ellipsis(3);
}

在 Vue 项目开发中,当我们想在 .vue 文件使用上述全局变量时,我们需要在每个文件手动 @import 'variable.scss'。我们可以在 vue.config.js (or webpack config file)中配置一下相关属性,它可以支持自动引入而无需我们手动 import。

// vue.config.js

module.exports = {
  css: {
    loaderOptions: {
      scss: {
        prependData: `@import "~@/styles/variables.scss";`
      }
    }
  }
};

话说其实 CSS 也有原生的自定义变量功能。

// 原生的自定义变量
:root {
  --red: #e03e3e;
  --blue: #0b6e99;
  --yellow: #dfab01;
  --gray: #9b9a97;
}
.dark-mode {
  --red: #ff7369;
  --blue: #529cca;
  --yellow: #ffdc49;
  --gray: hsla(195, 2%, 60%, 0.95);
}
body {
  color: var(--fg-color);
}

/* <body class="dark-mode"></body> */

原生自定义变量在一些场景下有奇效,比如动态换肤,如上代码所示,黑暗模式只需重新定制相关变量颜色,body 元素设置 dark-mode 类名即可。

不过在旧项目中使用需要注意,混用预编译语言和原生自定义变量会有开发上的心智负担,请酌情使用。

SCSS 变量形式初上手开发时可能会略为不习惯,但是写多了以后就会发现常用的颜色也就那么几个,遵照设计进行语义化变量取名,看设计稿有的时候就不用特地看取色,你会下意识的知道边框颜色一定是 $color-border,背景色一定是 $color-page-bg 诸如此类。

但是变量名辣么长,就这么一个个字敲出来多少还是有些呆,下面还会介绍几个实用的 VSCode 插件,可以很好地解决这个问题。

VSCode 插件

常用的插件就不多说了,我会介绍一些冷门好用插件。

vue-scss-variable-scan

这个功能就是配合上一节 SCSS 变量使用的,只要你打出相关变量的关键词,会给你自动提示项目里所有声明过的 SCSS 变量。

vue-scss-variable-scan

SCSS Everywhere

支持 html 代码中 class 属性的自动补全。

scss-everywhere

css-snippets

支持常用 CSS 属性的快捷补全。

css-snippets

代码片段

代码片段是 VSCode 默认自带的功能,我们可以根据自己的习惯编写一些常用代码片段,输入关键词便可自动补全对应代码,可以有效提示开发效率。

其实 VSCode 里许多关于语言 snippet 插件,本质上就是代码片段。

code-snippets

Vue 相关

v-img

项目里的外链图片的引入,我们一般会下意识地写如下代码。

<img :src="userInfo.Avatar" alt="头像" />

然而用户上传的可能是很大的原始图片,会有加载速度的问题。

项目里的 v-img 指令可以很好地解决这个问题,你可以指定图片的宽高,cdn 会根据尺寸返回相应压缩版本的图片,大大减少加载速度。

<img v-img="userInfo.Avatar" data-width="48" data-height="48" alt="头像" />

mixin/extend

mixin/extend 可以实现一定规模的粗粒度配置复用,适时使用可以避免很多 hard code ctrl+c/v。

它们基本原理都差不多,同名选项默认遵循合并原则,同名生命周期钩子函数将合并为一个数组依次调用。

不同点在于:

keep-alive

keep-alive 主要用于保留组件状态或避免重新渲染。

对于常用页面切换、详情页返回列表页这类场景,使用 keep-alive用户体验效果拔群

但是!它其实也隐藏着一些坑,它的缺点也是它的优点:

针对上面这些问题,我们可以灵活使用组件上的 include 属性,它支持字符串/正则表达式,只有名称匹配的组件会被缓存。(include 匹配的是组件 name/components 属性值)

<!-- 匹配以 -cached 为结尾的字符串 -->
<keep-alive :include="/(-cached)$/">
  <router-view class="root-router-view"></router-view>
</keep-alive>

API - Vue.js

模块/路由动态加载

我们可以根据项目特点,针对性地做一些动态加载优化。

组织项目根据角色可以划分为普通用户和管理者,据此我们可以将管理相关的路由,聚合成一组进行动态加载。这样普通用户进入组织并不会加载这部分代码。

// 路由动态加载
const wrapperView = () => import(/* webpackChunkName: "management" */ '@/pages/management/index.vue');
const profilePage = () => import(/* webpackChunkName: "management" */ '@/pages/management/profile.vue');
const orderPage = () => import(/* webpackChunkName: "management" */ '@/pages/management/order.vue');
const humanServicePage = () => import(/* webpackChunkName: "management" */ '@/pages/management/human-service.vue');
const workbenchPage = () => import(/* webpackChunkName: "management" */ '@/pages/management/workbench.vue');
const modulePage = () => import(/* webpackChunkName: "management" */ '@/pages/management/module.vue');

模块组件同理,我们可以将一些比较大的诸如 echarts ace-editor 这类第三方组件进行动态加载优化,它们的使用场景和频率都比较低,用到的时候再加载也不迟~

// 模块动态加载
Vue.component('markdown-editor', () => import('@/components/shared/markdown-editor.vue')); // markdownEditor
Vue.component('editor', () => import('vue2-ace-editor'));
[Lazy Loading Routes Vue Router](https://router.vuejs.org/guide/advanced/lazy-loading.html)

其它一些值得注意的点

复用

有些小功能逻辑,可能之前就已实现过,可以先试图搜索相关代码文件,直接 import 使用。

Moment.js

Moment.js 已经停止维护了,而且在 webpack 项目引入 Moment.js 时,它会在打包时默认附带本地化和时区的相关文件,需要配置 webpack 特殊处理去除。

可以使用 Day.js 替代,和 Moment.js 基本相同的 API,也不会附带庞大的时区文件,额外的功能也可以通过插件的方式引入。

Day.js · 2kB JavaScript date utility library

私有化

开发功能时需要兼顾公有云和私有化,部分功能是私有化独有或是需要特殊处理,可通过 $store.state.env.isOp 区分。

在进行某些变化性比较大的功能开发迭代时,可以和产品提前商量未来该功能的私有化策略,减少私有化的迁移成本。

iconfontのsymbol 引用

我们现在的项目关于 iconfont 的图标都是 font-class 引用,它有以下特点:

问题在于:它对于多色图标并不支持,这样就导致我们需要把多色图标单独下载为 svg 放入 assets 文件引用。单色图标和多色图标是分开引入的,管理起来并不是很方便。

其实看 iconfont 文档会发现它们还提供一种 symbol 引用,它有以下特点:

<!-- symbol 引用 -->
<style type="text/css">
    .icon {
       width: 1em; height: 1em;
       vertical-align: -0.15em;
       fill: currentColor;
       overflow: hidden;
    }
</style>

<svg class="icon"><use xlink:href="#icon-xxx"></use></svg>

不过。。。这玩意也是旧项目迁移成本比较大,可以在新项目尝试使用。

详见 iconfont 文档:

iconfont-阿里巴巴矢量图标库