森森记账是一款基于Vue/TypeScrip/Vuex/SVG/Echart实现的极简主义记账应用。是为了满足笔者自己的使用需求而独立开发的一款应用。该项目使笔者对TypeScript、Vuex、SVG等有了深入的理解。
本文将详细探讨开发项目中对下述概念的理解:
- 怎么使用vue2+TS的组合
- 全局数据管理是什么?Vuex怎么使用?
- 使用hash表改变数据结构,将数据分门别类。
- Vue中使用symbol的正确姿势
预览地址:dangxiadeliliang.gitee.io/money-websi…
TS+Vue2
Vue2对于TS的支持并不是很好,为此我们要使用带有装饰器的组件,而且其他的构造选项的使用方法也有所不同。
import Vue from "vue";
import { Component,Props } from 'vue-property-decorator'
@Component
export default class Account extends Vue {
@Prop(Number) xxx: number|undefined;
}
全局数据管理
笔者在开发该项目发现,多个页面间存在需要共享数据的需求,记账页、账目页、统计页之间需要共享用户的流水记录,其中包括了流水类型、选择的标签、金额、备注、记账事件。而记账页和管理标签页又需要共享所有标签的信息。这就使得自上而下的“单向数据流”反而变得复杂起来。
下图为流水记录
下图为标签的信息
所以所有对数据的增删改查和保存应该统一管理,放到一个文件中。而且调用有多种方式,例如将数据操作挂在window上,保证页面间的数据同步。
但是如果将所有的API都挂到window之上,过于依赖window,势必造成变量冲突。因此放到window上的store属性,或者不如独立出一个store的模块,将全局引入。
而Vuex为此提供了规范的用法,将组件的共享状态抽取出来,以一个全局单例模式管理。具备state\getter\mutation\action\module这些核心概念。
数据结构
怎样将原有的数据变成下图这个样子呢,它的核心就是数据结构。笔者使用这样了一个hash表轻松将这些数据进行了分门别类。
type HashTable ={title:string,total?:number,items:RecordItem[]}[]
SVG=>Symbol
过去引入Symbol需要引入经由转换好的js文件,这种方式一旦对项目库的svg进行增删改查,那么必然要重新生成js文件,非常麻烦。
引入svg-sprite-loader,我们就能直接将svg后缀的文件放到项目文件中,svg-sprite-loader会将所有后缀文件转化为symbol,并都包含在svg标签放到页面里。另外还可以利用svgo-loader为svg文件做更多自定义的优化。
安装
$ yarn add svg-sprite-loader
$ yarn add svgo-loader
配置webpack
// 在vue.config.js文件
const path =require('path')
module.exports = {
// 以下为svg的配置
chainWebpack: config =>{
const dir = path.resolve(__dirname, 'src/assets/icons')
config.module
.rule('svg-sprite')
.test(/\.svg$/)
.include.add(dir).end() // 包含 icons 目录
.use('svg-sprite-loader-mod').loader('svg-sprite-loader-mod').options({extract:false}).end()
.use('svgo-loader').loader('svgo-loader')
.tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]})).end()
config.plugin('svg-sprite').use(require('svg-sprite-loader-mod/plugin'), [{plainSprite: true}])
config.module.rule('svg').exclude.add(dir) // 其他 svg loader 排除 icons 目录
}
}
注意: 引入svg,使用require而非import,import会在编译时被treeeshaking掉,而reuire不会。
require('icons/money.svg')
使用svg
<svg>
<use xlinkHref="" /> // 填写svg的id,默认为svg的文件名
</svg>
随着svg的数量增多需要自动化的引入方式,我们需要将svg后缀的文件一次性引入
let importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext);
try {importAll(require.context('icons', true, /\.svg$/));} catch (error) {console.log(error);}
页面
从HTML到SCSS到TypeScript到模块化。
HTML是页面的骨架,首先对照设计稿把写出来HTML结构,在添加样式的时候再将多余的标签删除。
写SCSS。应注意设计稿中的背景颜色,常用字体类型等并将其设为全局样式。将常用样式(如字体类型,颜色)的变量写在helper.scss文件,且该文件只用于存放变量的声明代码。
TS。采用如下组件模板,深入理解ts的语法。ts的语法提示帮助开发者发现代码中的隐患,并且使得语法错误无法通过编译(编辑器提示、编译时报错)。
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator';
@Prop(Number)myProp:number|undefined;//括号内的类型是运行时,而冒号后是编译检查
@Component
export default class Types extends Vue {
}
</script>
模块化。每个文件应该主动减少重复,一般来说不应超过百行代码,并且应使各个模块的功能明晰,便于阅读和后期维护。
使用dayjs
js的内置对象Date操作起来非常麻烦,引入dayjs,功能与moment.js完全一致的情况下,仅有2kb的大小
总结
在开发的过程效率低下主要集中在处理两件事上,一是排除故障,二是酝酿JS代码
一是首先对报错做到详读,并利用调试技巧精准定位错误。不力则尽快通过去搜索,并把解决方案记录总结。
二是代码逻辑复杂时应该通过伪代码来理清头绪。
该项目使我对 TypeScript、webpack、SVG、Vuex等都有了深入的理解,开发的过程发现了很多知识和实践的不足。
该项目也会持续维护。