芒果记账项目踩坑记录

175 阅读1分钟

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('-')">