1. 项目简介
芒果记账是一款极简的记账应用,也是一款基于 Vue / Vuex / Vue Router / TypeScript 的单页面应用,本人独立设计和实现,期间遇到了很多 webpack 和 TypeScript面的问题,解决过程记录在本人的博客,源代码几乎完全用TypeScript实现,用到了装饰器语法。
2. 问题和解决方案
2.1 移动端键盘弹出引起的页面变形问题
会有软键盘问题是因为当软键盘弹起时,页面高度变了,导致页面变形
解决思路:设置当前页面的高度为页面高度即可
<template>
<div class="layout-wrapper" :style="{height: scrollerHeight}">
<div class="content">
<slot></slot>
</div>
<Nav></Nav>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import {Component} from 'vue-property-decorator';
@Component
export default class App extends Vue {
get scrollerHeight(){
return document.documentElement.clientHeight + 'px'
}
mounted() {
// window.onresize监听页面高度的变化
window.onresize = function() {
console.log(document.querySelector('#app').clientHeight)
}
}
}
</script>
<style scoped lang="scss">
.layout-wrapper {
display: flex;
flex-direction: column;
height: 100vh;
}
.content {
flex-grow: 1; // 让内容尽量占满
overflow: auto;
display: flex;
flex-direction: column;
}
</style>
2.2 点击事件传递
button里加了一个icon组件,当在button上写点击事件时,会没有用,因为点击的是icon组件里面的东西
解决方法:在icon组件里写一个$emit, 它被点击时,触发外面的点击事件
Labels.vue
<li v-for="(item, index) in this.outputTags"
:key="index">
....
<Icon :name="'delete2'" @click="deleteTag(item.id)"></Icon>
</li>
Icon.vue
// 触发点击事件,这样父组件就可以监听到了
<svg class="icon" @click="$emit('click', $event)">
<use :xlink:href="'#' + name"></use>
</svg>
2.3 localstorage保存对象的坑
第一次保存对象,第二次更新后保存对象,发现第二次的值覆盖了第一次的值
解决方法:要深拷贝!
saveAccount() {
this.accountList.push(this.account);
}
省拷贝:
saveAccount() {
// 深拷贝,重新parse成一个对象
const newAccount = JSON.parse(JSON.stringify(this.account))
this.accountList.push(newAccount);
console.log(this.accountList)
}
2.4 CSS最佳实践——deep语法
需求:多处使用type组件,但是css不同 解决方法:deep语法
<template>
<Layout>
<Types class="x"></Types>
</Layout>
</template>
<script lang="ts">
import Types from '@/components/Money/Types.vue';
</script>
<style scoped lang="scss">
.x ::v-deep li {
border: 1px solid red;
}
</style>
核心是这里:
<style scoped lang="scss">
.x ::v-deep li {
border: 1px solid red;
}
</style>
修改同一个组件CSS的最佳实践:用表驱动编程
<Layout>
<Types class-prefix="zzz" :value.sync="yyy"></Types>
</Layout>
<style scoped lang="scss">
::v-deep .zzz-item {
background: white;
&.selected {
background: #fed058;
}
}
</style>
<li :class="{[classPrefix + '-item']: classPrefix,
selected: value === '-' }"
@click="selectType('-')">
<span>支出</span>
</li>
<li :class="{[classPrefix + '-item']: classPrefix,
selected: value === '+' }"
@click="selectType('+')">
<span>收入</span>
</li>
@Prop(String) classPrefix?: string;
定义class:
:class="{[classPrefix + '-item']: classPrefix,
selected: value === '+' }"
注意如果想在对象的key里写变量,需要用中括号括起来 还可以进一步封装: 封装成一个函数,返回一个对象,还可以接受参数,方便遍历
liClass(type: string) {
return {
[this.classPrefix + '-item']: this.classPrefix,
selected: this.value === type
};
}
<li :class="liClass('-')"
@click="selectType('-')">