项目中遇到的问题_记账本Vue.js


大纲链接 §

[toc]

  • vue echarts 移动端 无 tooltip ⇧
  • 解决滚动条使页面跳动 ⇧
  • Bug: 按键.:按下时添加印记 删除时去掉印记 ⇧
  • 实现监听子组件事件 改变样式 ⇧
  • 隔离 ant-design-vue 的样式 污染了其它非 ant-design-vue 组件样式 ⇧
  • 用分隔符表示多位数字 自动添加逗号 万分位 千分位 小数处理 ⇧
  • 模拟触摸事件 ⇧
  • 判断文档高度 ⇧
  • vue计算属性(的返回值)能否用在动态绑定的自定义事件 @[computedValue] ⇧
  • 引入 SVG 遇到的问题 ⇧
  • 使用路径别名alias ⇧
  • 有颜色的图标 SVG格式的icon 初始化 填充色属性 ⇧
  • 在vue.config.js中报 eslint 错误怎么办? ⇧
  • 收集移动端 flex bug ⇧
  • 升级 此项目的依赖 涉及安全 ⇧
  • 移动端隐藏手机浏览器的地址栏、底部的菜单栏 ⇧
  • 滑动 Numpad 键盘(连按)报错 ⇧
  • 手机浏览器 中statistics仅当有内容的时候切换支出收入按钮才有效 ⇧

修复vue.config.js:本地预览 dist 目录发现 JS 路径错误

使用 yarn build 得到 dist 目录后,再用 serve -s dist 然后在本地浏览器打开 http://localhost:5000 会看到:

如图报错

  • 这是因为在本地环境,这个 JS 的路径不对
  • 虽然在 GitHub Pages 里,这个JS 的路径其实是对的
  • vue.config.js,其实已经考虑了这个问题
    • vue.config.js会在 production 环境(也就是 GitHub Pages 上)使用 /morney-3-website/ 作为路径前缀
    • 在本地使用 / 作为路径前缀

如图

  • 但实际情况是,本地的 dist 使用了 /morney-3-website/,正确的前缀应该是 /
  • 为什么 vue.config.js 考虑了这个问题,还是会出现这个问题呢?

怎么解决这个问题

步骤如下:

  • yarn add cross-env(这个 cross-envWindows 用户必须的,其他系统的用户装了它也没事,不会有任何副作用)
  • package.jsonscript字段里添加
    • "build:dev":"cross-env NODE_ENV=development yarn build"

注意行尾的逗号

如图所示

  • 使用 yarn build:dev 得到的 dist 即可在本地用 serve 预览
  • 使用 yarn build 得到的 dist 即可在 GitHub Pages 上正常预览

vue.confi.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// ESLint ignore first line
/* eslint-disable */
const path = require('path')

module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/meowney-0-website/'
: '/',
lintOnSave: false,
chainWebpack: config => {
const dir = path.resolve(__dirname, 'src/assets/icons') // 确定目录
config.module
.rule('svg-sprite')
.test(/\.svg$/) // .test(/\.(svg)(\?.*)?$/)
.include.add(dir).end() // 指定 仅包含 icons 的目录
.use('svg-sprite-loader').loader('svg-sprite-loader')
.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/plugin'), [{plainSprite: true}])
config.module.rule('svg').exclude.add(dir) // 其他 svg loader 排除 icons 目录
*/
},
// pluginOptions: {}
}


vue echarts 移动端 无 tooltip

  • 移动端无 hover 行为
  • 需要改变 tooltip: {triggeron: {'mousemove | click'}} 默认为mousemove,改为 click

判断 环境

1
2
3
4
5
6
7
8
9
...
  get triggerMethod(): 'click' | 'mousemove' {
    if (this.client === 'PC') {
      return 'mousemove';
    } else {
      return 'click';
    }
  }
...

返回 eCharts 选项

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  get myChartOption() {
    const keys = this.chartDataKeyValueList.map(item => item.key);
    const values = this.chartDataKeyValueList.map(item => item.value);
    return {
      xAxis: {
        type: 'category',
        data: keys,
        axisTick: {
          alignWithLabel: true
        },
        axisLine: {
          lineStyle: {
            color: '#666'
          }
        },
        axisLabel: {
          formatter: (value: string) => {
            return value.substr(5);
          }
        }
      },
      yAxis: {
        type: 'value',
        show: false
      },
      tooltip: {
        show: true,
        triggeron: this.triggerMethod,
        confine: true,
        position: 'top',
        formatter: '{c}'
      },
      series: [{
        symbol: 'circle',
        data: values,
        type: 'line',
        itemStyle: {
          borderWidth: 3,
          borderColor: '#aaa',
          color: '#666'
        },
        symbolSize: 12,
        showBackground: true
      }],
      animationDuration: 888,
      grid: {
        left: 3,
        right: 0,
        top: 8
      }
    };
  }

在模板中使用

1
2
3
...
    <ECharts class="echarts" :options="myChartOption" ref="vChartContent"/>
...

Statistics.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<template>
  <Layout class="statistics">
    <HeaderBar :header-title="'统计'" router-path="/money"></HeaderBar>
    <Tabs class-prefix="type" :data-source="recordTypeList" :type.sync="type"/>
    <div class="echarts-wrapper" ref="vChartWrapper">
      <ECharts class="echarts" :options="myChartOption" ref="vChartContent"/>
    </div>
    <ol v-if="groupedList.length > 0">
      <li v-for="(group, index) in groupedList" :key="index">
        ...
      </li>
    </ol>
    <div v-else class="noResult">
      目前没有相关记录
    </div>
  </Layout>
</template>

<script lang="ts">
import...;

@Component({
  components: {HeaderBar, Tabs, ECharts}
})
export default class Statistics extends Vue {
  type = '-';
  recordTypeList = recordTypeList;
  client = getClientWidth();

  get triggerMethod(): 'click' | 'mousemove' {
    ...
  }

  get chartDataKeyValueList() {
    ...
    return array;
  }

  get myChartOption() {...}

  // 读取 记录列表 // computed
  get recordList() {
    // 包括 支出和收入
    return this.$store.getters.recordList;
  }

  // 计算 分组列表 分为 支出/收入
  get groupedList() {
    ...
    return result;
  }

  // 显示项目 title 标签组合
  tagToString(tags: Tag[]) {
    return tags.length === 0 ? '无' : tags.map(tag => tag.name).join('、');
  }

  // 显示标题为 日期分组
  /**
   * @param {string} someday: from group.title YY-MM-DD
   * @return {string} formattedDate:
   */
  showDay(someday: string) {.
  ...
    return formattedDate;
  }

  // hooks
  protected beforeCreate(): void {
    this.$store.commit('fetchRecords');
  }

  mounted() {
    const echartDiv = (this.$refs.vChartWrapper as HTMLDivElement);
    setTimeout(() => {
      echartDiv.scrollLeft = echartDiv.scrollWidth;
      echartDiv.scrollBy(echartDiv.scrollWidth, 0);
    }, 800);
  }
}
</script>

<style lang="scss" scoped>
...
</style>


解决滚动条使页面跳动

  • 在内容超过父元素的盒子上,添加属性 overflow-y: auto; overflow-x: hidden;

参考文章:


Bug: 按键.:按下时添加印记 删除时去掉印记

  • 伪元素,样式
  • 判断点击目标
    • 是点击目标,就添加样式,计算属性

子组件 NumpadButton.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<template>
  <button :class="['basic-btn', {'current': buttonIndex === curIndex}]"
          :data-index="buttonIndex">
    {{ buttonText }}
    <slot/>
  </button>
</template>

<script lang="ts">
import Vue from 'vue';
import {Component, Prop} from 'vue-property-decorator';

@Component
export default class NumpadButton extends Vue {
  @Prop(String) buttonText!: string;
  @Prop(Number) buttonIndex!: number;
  @Prop({type: Number, default: 13}) curIndex!: number;
}
</script>

<style lang="scss" scoped>
$bg: #f2f2f2;
.basic-btn {
  width: 100%;
  height: 100%;
  background: transparent;
  border: 1px solid transparent;
  transition: border-color .25s;
  position: relative;
  &.current::after {
    content: '';
    display: block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #f0f0f0;
    box-shadow: 0 0 0 #cccccc,
    0 0 0 #ffffff,
    3px 3px 3px #cccccc inset,
    -3px -3px 3px #ffffff inset;
    position: absolute;
    top: .5em;
    right: .5em;
    }
    &...
  }
</style>

父组件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<template>
  <div class="numpad">
    <div class="output">{{ localOutput || '0' }}</div>
    <div class="buttons"
         @[clientEvent]="checkBtn($event);handleButtonFn($event)"
         @mousemove="showSearchlight"
         :style="searchlightStyle">
      <numpad-button
          v-for="(item, index) in numPadText"
          :data-bundle-event="item.bundleEvent"
          :button-index="index"
          :curIndex="curIndex"
          :key="item.id"
          :class="{ok: item.id === 'ok', zero: item.id === 'zero'}"
          :button-text="item.text">
        <Icon v-if="['num', 'dot'].indexOf(item.name) === -1" :name="item.id"/>
      </numpad-button>
    </div>
  </div>
</template>

<script lang="ts">
import {Component} from 'vue-property-decorator';
import NumpadButton from '@/components/Money/numpad/NumpadButton.vue';
import {mixins} from 'vue-class-component';
import SearchLight from '@/mixins/searchLight';
import OperateNumpad from '@/mixins/operateNumpad';

@Component({
  components: {NumpadButton}
})
export default class Numpad extends mixins(SearchLight, OperateNumpad) {
  // 默认当前初始下标
  curIndex = 12;
  // 默认绑定事件
  eventName = 'click';

  // 返回当前选中按钮的下标
  checkBtn(e: UIEvent) {
    const target = e.target as HTMLElement;
    const className = target.className;
    const index = parseInt(target.dataset.index || '12', 10) || 12;
    if(className === 'basic-btn') {
      this.curIndex = index;
    }
  }

  // 判断客户端尺寸 返回对应的事件类型
  get clientEvent() {...}

  // 给不同的按钮绑定对应事件的处理函数
  handleButtonFn(e: TapEvent) {...}

  // 数字键盘文字图标数据
  numPadText = [...];

  // 格式化显示金额逻辑
  get localOutput() {...}
}
</script>

<style lang="scss" scoped>
...
</style>


实现监听 子组件事件 改变样式

  • 绑定样式 :class="['basic-btn', {'current': buttonIndex === curIndex}]"
  • 设置生效时的样式 .current { background: red; }
  • 绑定事件方法 @click="selectBtn(buttonIndex)"
  • 定义方法发布通知给父组件 this.$emit('selectBtn', buttonIndex)

在Vue中改用事件代理

  • 父组件 通过 事件冒泡机制 代理子组件的点击事件事件
    • 需要传入 事件对象 $event
    • 在方法参数中 接收事件对象 参数 e: Event
    • 解构 事件对象目标(事件原对象)target = e.target
  • 去除子组件中的 点击事件监听及对应方法
  • 子组件中 加上 HTML5 的自定义属性 :data-index="buttonIndex"
    • 在父组件中获取dataset属性:target.dataset.index;
    • 注意取到的属性值为 字符串类型,在这里需要转换为 数字类型 parseInt(target.dataset.index, 10)

注意 HTML5 的自定义属性 HTMLElement.dataset

  • 在标签中设置的 属性data-xxx-xxx 只可以包含 字母,数字 和下面的字符:
    • dash (-), dot (.), colon (:), underscore (_)
  • 此外不应包含 ASCII 码大写字母 形成的驼峰式命名
  • 比如 <numpad-button :data-bundle-event="item.bundleEvent">
  • HTML5自定义属性前缀 data-xxx
    • 解释:HTML规定可以为元素添加非标准的属性,但要添加前缀 data-
    • 目的:为元素提供与 渲染无关 的信息,或者语义信息。
    • 属性可以任意添加和命名,只要以 data- 开头
    • 访问:添加自定义属性之后,可以通过元素的 dataset 属性来访问自定义的值。

使用 js 操作dataset注意:

  • 在添加或读取属性的时候需要去掉前缀 data-
  • 如果属性名称中还包含连字符(-),需要转成 驼峰命名 方式
  • 但如果在CSS中使用选择器,需要使用 连字符格式

修改事件代理 辨别子组件内部元素是否为 目标元素e.target

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
...
  handleButtonFn(e: TapEvent) {
    let target = e.target as HTMLElement;
    while (!target!.matches('button')) {
      // 向外寻找父节点
      target = target.parentNode as HTMLElement;
    }
    const bundleEvent = target!.dataset.bundleEvent;
    this[bundleEvent as BundleEventString](e);
  }
...

小结

  • 子组件利用 HTML5的自定义属性 dataset 可以传递数据给父组件
  • 特别是当子组件是通过v-for循环渲染形成的,且需要事件代理

隔离 ant-design-vue 的样式 污染了其它非 ant-design-vue 组件样式

参考文章


用分隔符表示多位数字 自动添加逗号 万分位 千分位 小数处理

  • 用占位数字处理 fraction 部分 Int 部分
  • 补零显示
  • '12345678'.replace(/(\d)(?=(?:\d{4})+$)/g, '$1,')
  • 先行断言(正向预查)const [outPutInteger,] = this.output.match(/\d+(?=\.\d{2})/g) || '0';
    • 会漏判
    • 直接改为const outPutInteger = Math.trunc(Number(this.output));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
...
  // 实时显示 金额
  showLocalAmount = true;

  get localOutput() {
    // 分别 存 整数部分(integer part) 和小数部分(decimal part)
    const outPutInteger = Math.trunc(Number(this.output)).toString();
    const [outPutDecimal = '.00'] = this.output.match(/\.\d{2}/g) || '';
    const localAmount = outPutInteger
        .replace(/(\d)(?=(?:\d{4})+$)/g, '$1,') + outPutDecimal;
    console.log('this.output');
    console.log(this.output);
    console.log('outPutInteger');
    console.log(outPutInteger);
    console.log('outPutDecimal');
    console.log(outPutDecimal);
    console.log('-------');
    console.log(outPutInteger);
    return this.showLocalAmount ? localAmount : this.output;
  }

  checkInputNum(button: HTMLButtonElement, input: string, event: TapEvent) {
    // '0'开头的逻辑
    if (['0'].indexOf(this.output) !== -1) {
      // 输入不含'.' 的数字 1234567890
      if ('0123456789'.indexOf(input) >= 0) {
        return this.output = input;
      } else {
        // 输入 '.' 字符 '0.'
        return this.output += input;
      }
    }
    // '.'的逻辑
    const dotIndex = this.output.indexOf('.');
    // 存在'.'的情况 // 特殊 '0.'  不存在单独'.'
    if (dotIndex >= 0) {
      this.showLocalAmount = false;
      // '.' 判断重复输入
      if (input === '.') {return;}
      // 判断字符 为 '.'开头
      if (dotIndex === 0) {return this.output = '0.';}
      // '.'限制小数位 2位
      if (this.output.slice(dotIndex, -1).length > 1) {return;}
    }
    // 限制显示数字长度
    if (this.output.length >= 9) {
      alert('你的小目标金额超过喵内记账记录范围');
      return this.removeNum(event, -3);
    }
    return this.output += input;
  }

  inputNum(event: TapEvent) {
    const button = event.target as HTMLButtonElement;
    const input = button.textContent?.trim() as string;
    if(input === '.') {
      this.showLocalAmount = false;
    }
    this.checkInputNum(button, input, event);
  }

  removeNum(event: TapEvent, number = -1) {
    this.showLocalAmount = false;
    this.output = this.output.slice(0, number);
    if (this.output === '') {
      this.clearNum();
    }
    return this.output;
  }

  clearNum() {
    this.showLocalAmount = true;
    return this.output = '0';
  }
...

参考


模拟触摸事件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  /* eventType is 'touchstart', 'touchmove', 'touchend'... */
  sendTouchEvent(x, y, element, eventType) {
    const touchObj = new Touch({
      identifier: Date.now(),
      target: element,
      clientX: x,
      clientY: y,
      radiusX: 2.5,
      radiusY: 2.5,
      rotationAngle: 10,
      force: 0.5,
    });

    const touchEvent = new TouchEvent(eventType, {
      cancelable: true,
      bubbles: true,
      touches: [touchObj],
      targetTouches: [],
      changedTouches: [touchObj],
      shiftKey: true,
    });

    element.dispatchEvent(touchEvent);
  }

  myElement = document.body

  created() {
    this.$store.commit('fetchRecords');
  }

  /*
  mounted() {
    // hideMenuBar();

    this.sendTouchEvent(150, 150, this.myElement, 'touchstart');
    this.sendTouchEvent(150, 200, this.myElement, 'touchmove');
    this.sendTouchEvent(150, 150, this.myElement, 'touchmove');
    this.sendTouchEvent(150, 150, this.myElement, 'touchend');
  }
  */

踩坑:解决移动端点击Input呼出键盘页面被顶起和压缩问题

  • unclosed
  • 表明当前文档的渲染模式是怪异模式/混杂模式还是标准模式。
  • document.compatMode MDN

判断文档高度

  • 判断文档高度需要减去底部栏高度
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
getWindowConfig () {
let windowWidth = window.innerWidth;
      let windowHeight = window.innerHeight;
      if (typeof windowWidth !== 'number') {
        if (document.compatMode === 'CSS1Compat') {
          windowWidth = document.documentElement.clientWidth;
          windowHeight = document.documentElement.clientHeight;
        } else {
          windowWidth = document.body.clientWidth;
          windowHeight = window.body.clientHeight;
        }
      }
      return {
        windowWidth: windowWidth,
        windowHeight: windowHeight
      }
}
...
    this.$nextTick(() => {
      const pageInfo = getWindowConfig();
      (this.$refs.app as HTMLElement).style.height
        = (pageInfo.windowHeight - 46) + 'px';
    });

vue计算属性(的返回值)能否用在动态绑定的自定义事件 @[computedValue]

提问

  1. 代码链接 https://github.com/xmasuhai/money-1 定位文件 src/components/Money/Numpad.vue 第8、48行
  2. 运行步骤为 yarn install; yarn serve
  3. 问题是:怎么按条件 给一个元素 绑定 多种事件监听,以及不同的回调函数
  4. 具体是:按照客户端宽度(document.documentElement.clientWidth )判断 分别监听 buttonclicktouchStart事件;并且根据不同的button 设置不同的 回调函数
  5. 目前的错误写法是 @[clientEvent]="inputNum";其中 clientEvent 是计算属性,当页面处于手机大小时,点击按钮不起作用
  6. vue计算属性(的返回值)能否用在 动态绑定事件名上 @[computedValue]

debug

  • 事件的大小写写错了touchstart 错误地写成了touchStart
  • 通过浏览器的事件侦听器看到大小写错误

引入 SVG 遇到的问题

vue2中引入 .svg 图标,使用iconfont图标库


使用路径别名alias

  1. svg-sprite-loader 会导致 cssimport ~@WebStorm 里报错,如果在 github issue 被提出,官方未修正 bug,可以自己发布一个svg-sprite-loader-mod,安装到依赖中
  2. 使 webStorm 认识组件样式标签里引入的文件 @import "~@/assets/style/global.scss";,需在设置中搜索 webpack,配置 webpack configuration file,为本地项目路径/node_modules/@vue/cli-service/webpack.config.js,在vue/cli文档中有说明

遇到最难的技术问题是什么

  • svg-sprite-loader引入SVG icon作为symbol图标
  • 发现webStorm引入scss文件时,提示错误
  • 配置 webpack
  • 查看svg-sprite-loadergithub issue,发现相同问题
  • 提出github pr
  • npm publishmergefork自己动手解决问题
  • 在项目依赖中引入自己的包

有颜色的图标 SVG格式的icon 初始化 填充色属性

可以记录为项目遇到的困难 bug 踩坑

  • SVG 源文件中<path /> fill属性控制填充色
  • 自动批量去除字体图标的填充色
  • 安装yarn add --dev svgo-loader
  • 配置vue.config.js
  • 注意版本svgo-loader@2.2.1,最新版会失效

需要配置vue.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const path = require('path')
module.exports = {
  lintOnSave: false,
  chainWebpack: config => {
    const dir = path.resolve(__dirname, 'src/assets/icons') // 确定目录
    config.module
      .rule('svg-sprite')
      .test(/\.svg$/) // .test(/\.(svg)(\?.*)?$/)
      .include.add(dir).end() // 指定 仅包含 icons 的目录
      .use('svg-sprite-loader').loader('svg-sprite-loader')
      .options({extract: false}).end() // 不解析出文件
      .use('svgo-loader').loader('svgo-loader') // SVG优化插件
      .tap(options =>({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]})).end() // 去除 SVG填充色属性
    config.plugin('svg-sprite').use(require('svg-sprite-loader/plugin'), [{plainSprite: true}])
    config.module.rule('svg').exclude.add(dir)
  },
}
  • 无视SVG自带填充色,都由css控制填充颜色
  • 替代方法为在引入svg前就批量清除填充色

相关svg文章

库和文档


使用svg-sprite-loader引入icon

  • 使用svg-sprite-loader
  • 安装svg-sprite-loader:
  • yarn add svg-sprite-loader -D
  • 配置vue.config.js
  • shims-vue.d.ts中添加svg声明
  • 将在iconfont下载的svg图标引入到Nav组件中

vue.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const path = require('path')

module.exports = {
lintOnSave: false,
chainWebpack: config => {
const dir = path.resolve(__dirname, 'src/assets/icons') // 确定目录
config.module.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
.rule('svg-sprite')
.test(/\.svg$/)// .test(/\.(svg)(\?.*)?$/)
.include.add(dir).end() // 指定 仅包含 icons 的目录
.use('svg-sprite-loader').loader('svg-sprite-loader')
.options({extract: false}).end() // 不解析出文件

config.plugin('svg-sprite').use(require('svg-sprite-loader/plugin'), [{plainSprite: true}])
config.module.rule('svg').exclude.add(dir)
}
}

shims-vue.d.ts

1
2
3
4
5
6
7
8
9
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}
declare module '*.svg' {
const content: string;
export default content;
}

Nav.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<nav>
<router-link to="/money">记账</router-link>
|
<router-link to="/labels">标签</router-link>
|
<router-link to="/statistics">统计</router-link>
</nav>
</div>
</template>

<script lang="ts">
import x from '@/assets/icons/bills.svg';

console.log(x);
export default {
name: 'Nav'
};
</script>

  • svg-sprite-loader 会导致 cssimport ~@ WebStorm 里报错

vue.config.js中报 eslint 错误怎么办?

  • /* eslint-disable */ 把这句话添加到 vue.config.js 的第一行即可

eslint error ‘xxx‘ is not defined no-undef报错解决

  • eslint 报的错,全局中找不到这个变量,但是大多数情况下,代码里已经定义了或者引用了
  • 需要配置eslint
    • eslintrc.js里面添加如下代码:
1
2
3
4
5
...
    "globals": {
        "xxx": true  // xxx -> 报错的变量
    },
...

收集移动端 flex bug

  • google移动端 flex bug

双击界面放大后不能缩小

  • meta viewport设置

我发现一个bug,vue中使用 @input 监听输入框按下键盘上的'1',中文输入法状态下会触发两次 @input,英文输入法状态下只会触发一次 @input

  • 已解决,需要设置 @compositionstart@compositiοnend
  • 参考文章:https://juejin.cn/post/6844903614440439822

我在导航栏那碰到了一个问题,就是svg位置占在那但是样子显示不出来,控制台也没报错,但是我把组件里的use写死就能显示


https://www.yuque.com/jiangdaoran/gt5tss/vygbi0#rG3tl,可以参考下我之前做的笔记,其他TS的课程都没讲把类型放到custom.d.ts,还得到处导入类型

那好好设计一下,数字键盘也隐藏掉 选用用一个键盘组件,不用input 因为你们用的都是input呀!input会调用手机自带的键盘 http://tian_qi0.gitee.io/bill 我这个好像不会顶起来 https://www.yuque.com/nizai-g45g4/coanny/tgedbb https://www.yuque.com/docs/share/252f3bda-8f23-4eda-baf5-b5434a3cc969?# 《钱多多记账-解决移动端点击Input弹出键盘,页面被顶起和压缩问题》


升级 此项目的依赖 涉及安全

  • vulnerabilities on xmasuhai/money-1's default branch (2 moderate). To find out more, visit: remote: https://github.com/xmasuhai/money-1/security/dependabot
  • ndoe14 regression 衰退bug
    • No "exports" main resolved in @babel/...
  • solution
    • update @babel deps to v7.8.7+
    • use Node < 13.9
  • 确认是否提交过当前代码
  • rm -rf node_modules/
  • rm yarn.lock
  • yarn install

移动端隐藏手机浏览器的地址栏、底部的菜单栏

  • 浏览器的地址栏以及下面的工具栏的问题会导致在有的手机上面的页面显示不全

滑动 Numpad 键盘(连按)报错

  • TypeError: target.matches is not a function

手机浏览器 中statistics仅当有内容的时候切换支出收入按钮才有效

  • 设置用户提示内容 项目某项功能耦合


参考文章

相关文章


  • 作者: Joel
  • 文章链接:
  • 版权声明
  • 非自由转载-非商用-非衍生-保持署名