组织版前端开发心得分享①
这是一篇面向公司内部的分享,部分方案具有时效性。
SCSS 变量
使用 SCSS 变量主要解决的问题:
- UI 迭代只需修改颜色变量,避免大量修改各个文件内的色值。
- 避免 UI 多次颜色革命产生各种主色混杂在各个文件的问题。
- 复用项目内常用的 CSS,收敛和规范使用色值。
我们可以把常用的颜色统一用语义化变量来替代,此处我们定义为 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 变量。

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

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

代码片段
代码片段是 VSCode 默认自带的功能,我们可以根据自己的习惯编写一些常用代码片段,输入关键词便可自动补全对应代码,可以有效提示开发效率。
其实 VSCode 里许多关于语言 snippet 插件,本质上就是代码片段。

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。
它们基本原理都差不多,同名选项默认遵循合并原则,同名生命周期钩子函数将合并为一个数组依次调用。
不同点在于:
- extend 适用于对相同逻辑的基类组件进行扩展派生。
- mixin 适用于通用/值得复用的基础选项逻辑混入使用。
keep-alive
keep-alive 主要用于保留组件状态或避免重新渲染。
对于常用页面切换、详情页返回列表页这类场景,使用 keep-alive用户体验效果拔群。
但是!它其实也隐藏着一些坑,它的缺点也是它的优点:
- 如果多个缓存页面引入了某个公共组件,那每个缓存页面其实都会缓存着一个公共组件实例!
- 缓存组件的 eventBus/定时器/watch 在页面路由离开后也可能会触发,一定需要注意回收处理。
- 诸如新建xxx的表单页之类,每次进入都需要手动重置默认值。
针对上面这些问题,我们可以灵活使用组件上的 include 属性,它支持字符串/正则表达式,只有名称匹配的组件会被缓存。(include 匹配的是组件 name/components 属性值)
<!-- 匹配以 -cached 为结尾的字符串 -->
<keep-alive :include="/(-cached)$/">
<router-view class="root-router-view"></router-view>
</keep-alive>
模块/路由动态加载
我们可以根据项目特点,针对性地做一些动态加载优化。
组织项目根据角色可以划分为普通用户和管理者,据此我们可以将管理相关的路由,聚合成一组进行动态加载。这样普通用户进入组织并不会加载这部分代码。
// 路由动态加载
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 使用。
src/components/global内含 Vue 全局的组件,指令,过滤器,实用函数。src/utils顾名思义是一些通用的工具函数。src/App.vue有一些业务相关/模板内快速调用的方法。src/styles/variable.scss内包含 CSS 相关的可复用代码。- …
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 引用,它有以下特点:
- 兼容性良好,支持ie8+,及所有现代浏览器。
- 使用 class 来定义图标,所以当要替换图标时,只需修改 class 名。
- 本质上还是使用的字体,所以多色图标还是不支持的。
问题在于:它对于多色图标并不支持,这样就导致我们需要把多色图标单独下载为 svg 放入 assets 文件引用。单色图标和多色图标是分开引入的,管理起来并不是很方便。
其实看 iconfont 文档会发现它们还提供一种 symbol 引用,它有以下特点:
- 支持多色图标了,不再受单色限制。
- 通过一些技巧,支持像字体那样,通过
font-sizecolor来调整样式。 - 浏览器渲染 svg 的性能一般。
- 因为本质是引用 svg 集合,图标多的时候大小会是个问题。
<!-- 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 文档: