熊猫记账(panda-booking)是一款简约、轻量的记账应用,也是一款基于Vue、VueRouter、Vuex、TypeScript的单页面应用。源码链接 项目预览
参考了市面上多款记账应用的设计界面,独立实现完成。下面记录一些实现过程中遇到的问题:
1.svg图标的引用
参考文档:https://juejin.im/post/59bb864b5188257e7a427c09
我用的是阿里的iconfont,根据官方文档可以使用symbol方式引入项目下面生成的iconfont.js,加入通用css,然后在页面中用svg标签加相应的图标的class名就可以了,但是这种方式的缺点也很多,参考文档中列举了一些,我自己使用过程中的感受就是不方便,每次在图库新添加图标的时候要重新生成引入新的js文件,找对应的图标也很不方便。于是就搜索解决办法,可以使用webpack的loader解决:
- 安装依赖
npm install svg-sprite-loader --save-dev
2.全局配置vue.config.js
const path = require('path')
module.exports = {
lintOnSave: false,
chainWebpack: config => {
const dir = path.resolve(__dirname, 'src/assets/icons') //svg文件位置
config.module
.rule('svg-sprite')
.test(/\.(svg)(\?.*)?$/)
.include.add(dir).end() //包含icon目录
.use('svg-sprite-loader-mod').loader('svg-sprite-loader-mod').options({extract: false})
// .end()
// .use('svgo-loader').loader('svgo-loader') // 自动删除svg的fill
.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目录 不然会有loader冲突
}
}
中间注释掉的是自动删除svg文件fill属性的,如果项目很多svg而且不需要填充颜色的时候可以使用,也可以手动删除fill属性。
3.svg-sprite-loader通过icon引入了svg,然后在svg里面做了symbol,然后就可以使用标签了
import name from "@/.../icons/name.svg"
<svg>
<use xlink:herf="#name"/>
</svg>
4.解决一个引用svg文件的TS报错
在shims-vue.d.ts文件中声明一下
declare module "*.svg" {
const content: string;
export default content;
}
5.封装了一个Icon组件,解决每次使用都要import的问题
// 声明一个引入方法,遍历指定文件夹里面的所有文件
const importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext);
try {
// 指定icons文件夹,指定以.svg结尾的文件
importAll(require.context('../assets/icons', true, /\.svg$/))
} catch (error) {
console.log(error);
}
2.classPrefix
之前做了个Layout组件,包含底部导航Nav组件和上面的视窗,后来做记账页面的时候碰到了布局问题,要Layout变成flex布局,开始就直接传的class的前缀名直接加Layout,后来想想如果Layout的子元素也需要改样式那岂不是还要传别的名字来增加子元素的class,解决办法就是不写死class,给Layout绑定动态class:
<div class="content" :class="classPrefix && `${classPrefix}-content`">
然后父组件定义classPrefix并传过去就好了。
3.父子组件通信,子组件的input标签给v-model,ts怎么获取input的value
解决办法:
1.用原始方法,动态绑定value,然后监听value改变触发改变value函数并通过$emit传给父组件
<input type="text" :value="notes" @change="onValueChang($event.target.value)" placeholder="在这里输入备注">
2.子组件给ref属性,ts可以通过$refs获取DOM元素
<input type="text" v-model="value" ref='input' />
this.$refs.input.value //获取input的value
4.需求是记账页面点击ok账单记录成功清空备注内容并数字清零,没成功备注和数字不变
因为一开始没考虑失败情况,方法都放在了子组件里面,后果就是点击了ok后不管有没有成功都会清空,解决办法是清空的触发由父组件来执行,但是又需要去触发ok的点击事件,所以这里就又出现了一个问题:父组件怎么调用子组件的方法。
网上找到了两种方法:
1.如果是直接子组件可以使用$children
this.$children('组件名').子组件方法
2.不是直接子组件就给ref属性获取子组件
this.$refs.给的ref属性名.子组件方法
按照上述方法操作后发现子方法ts会报错,后来搜索解决办法要给前面指定类型:
(this.$refs.给的ref属性名 as HTMLFormElement).子组件方法
报错就没了
5.用::v-deep解决复用组件的内部样式问题
复用组件全局样式修改会影响别的页面效果,但是scoped又改不了它的样式,这时就可以在scoped里用::v-deep深入到组件里面,改组件在当前组件的样式。
6.引入dayjs库辅助处理账单的时间显示问题
几个主要方法:
当前时间
dayjs()
时间字符串
dayjs('2018-06-03')
时间戳
dayjs(1528361259484)
检查一个 Dayjs 对象是否和另一个 Dayjs 对象时间相同
dayjs().isSame(dayjs())
格式化
dayjs().format()
dayjs().format('YYYY-MM-DD dddd HH:mm:ss.SSS A')
减少时间并返回一个新的 Dayjs() 对象
dayjs().subtract(7, 'year')
dayjs().subtract(7, 'month')