点滴记账 项目进展记录

136 阅读17分钟

UI不错,抄

vue-create配置如下

《面试》使用了类组件

《Vue相关》@ === src

js文件 ===> import HelloWorld from '@/components/HelloWorld.vue'

scss文件 ===> @import '~@/assets/styles/test';

ctrl+鼠标左键 即可进入对应文件 import Test from '@/components/Test'

《其他》webpack.config.js是关于webpack这个工具的配置文件

作用是管理一些代码规范,比如说

《其他》vue.config.js是关于本项目如何去使用webpack的配置文件

《webstorm》webstorm 自动引入组件

《构建思路》导航栏在特定页面是不需要出现的

  • 比如404页面还需要底部有个导航栏干嘛?因此就把导航栏包装成一个nav组件分别导入到页面中去
  • 第二种方式是在main.js中全局引入Vue.component

《面试》面试官问你,你工作中遇到比较难的问题是什么?--- 《大问题1》

其实重点在于你如何讲一些缺陷方式不断优化,讲述你的思考过程以及优劣势让对方带入

《面试 构建思路》我是如何正确构建导航栏的?思考过程是什么? --- 《大问题1》

最初我用router-link写导航的时候是写在App.vue的也就是容器组件上,但是这样做的话会有个问题,有些页面类似404页面是不需要有导航栏存在的,所以后来选择把导航栏单独做一个组件,分别在用到的页面中引入一遍,但是这样的话又有点重复有点麻烦,我最后决定在main.js中只引入一遍,把导航栏当作一个全局组件这样在单独的页面中就不需要重复引入了直接使用即可。

《css》 手机页面手机端不要用fixed 定位而是用flex --- 《大问题1》

准备以下fixed定位有哪些问题,上网搜一下,然后讲一下flex布局的优势

《Vue》style中的scoped标签作用是什么,让你的组件属性仅仅作用于此组件中

  • 会给你当前的组件在渲染时加上一个 data-v-xxxxx
  • 会给你组件每个属性的后面也都添上[data-v-xxxxx]

《webstorm》webstorm 中使用git可视化工具的前置是要配置完成git的路径

《webstorm》如果你用webstorm-git可视化工具提交代码的时候才会eslint检查报错

  • 当然前提是你在vue-cli脚手架初始化时配置了eslint当commit的时候

《Vue》能用scoped的地方都用scoped这样就可以保证你的样式class名非常简单不被全局重复

《css》只对某个元素设置flex-grow:1, 就能把多余空间都给这个元素

《构建思路》flex布局如何实现导航栏固定在底部??

flex布局》--已实现Money页面的flex布局, Money页面整体是一个flex容器,其中两部分:内容与底部导航栏。
设置内容flex-grow:1,超出部分调用滚动条;底部则不用设置,只要将容器设置flex布局且容器高度是满屏即可让导航栏固定在底了。

《Vue》把大量重复的代码放进全局组件,不同的地方用slot接收参数

  • 插槽的用法就和函数传参一样
  • 先把大量重复的代码放进一个组件中,在全局main.js中注册这个组件

《项目相关》typescript不让你直接import svg

vue项目中使用svg会非常麻烦,SVG使用思路和项目路程有待整理

Vue项目中的css reset代码就写在根组件App.vue中

vue-router中的active-class可以控制点选导航栏图标之后的css变化

<router-link to="/labels" class="item" active-class="selected">

《webstorm》webstorm 配合 包围的emmet 小技巧

App.vue中的样式中的#app指的是初始化时侯的App.vue的wrapper有个id="app"

《Vue》全局的字体相关初始化样式是写在App.vue中的#app{}中

《Vue》css reset则放在src/assets/styles/reset.scss

字体的通用方案就是"font.css"这个库

zenozeng.github.io/fonts.css/

scss字体变量或者是别的scss变量则放在src/assets/styles/helper.scss

helper.scss里面放的是样式的变量,函数和mixins

scss语法

  • tags里面的XXX ,写法是当中是有空格的
  • tags本身还有一个selected,写法是当中没有空格

《css》所有的button和input里的字体都是默认不会继承祖先的。因此要手动reset

《flex》如何让字在一行中垂直居中 两种方式,最好还是用flex布局

  • 首推flex布局
  • 高度和行高等值

《flex》flex-grow用的很频繁 常用于占宽度和高度

去除input框被选中时的外轮廓

display:flex 设置容器自动就会把容器像block一样撑满一行的

不要用border去制作selected情况下的下边框效果,切换选中状态时会影响文字

选中时出现下边框的效果 应该使用伪元素+绝对定位

只要是等宽字体,就用monospace即可

monospace的意思就是系统里有什么等宽字体就用什么等宽字体,等宽字体就是编程字体

利用scss的占位符去书写clearFix 清除浮动类,百分号语法就是占位符

  • 好处是在于,你让html与css之间拆分得更明显,因为不然的话你就要写一个class在html结构上了
  • helper.scss中
  • 相当于把.buttons这个类名拷贝一份到helper.scss中去

《面试》面试官问 你scss用过哪些功能?

  • 变量
  • 占位符用于消除浮动,好处不用给html结构加class,让html与css之间的结构拆分得更明显
  • darken 颜色函数,让颜色变暗百分之4

《调试相关》使用box-shadow的时候自己利用chrome可视化工具去调试即可

《Vue相关》: 如何父给子传样式? 阿里团队的写法如下!!

《其他相关》设计稿是静态的,但是按照设计稿去制作的时候会发现因为内容过多导致的页面布局的变化,这个是麻烦点

《思考》为什么flex布局这里总是用到column-reverse,为什么元素要倒着写?

  • 由于flex布局的原因,参与布局的元素是用不到height这个属性的,而是用flex-grow去撑满的、
  • 下图写明了1234四个区域代表的一整个区域是content区域
  • html书写顺序也是1-2-3-4,会导致如下情况:4区下面还有一大片空白
  • 解决对策:把1区设置flex-grow:1,让它拿走全部剩余区域
  • 但是为了1区中的上半部分能够下沉,于是我再把1区设置为flex布局,把1区flex方向设置为反向column;
  • 思路是对的,但是html的结构也会颠倒了,于是再手动颠倒一下html的结构即可
  • 那么最终出来的效果就是,即1区拿掉剩余高度,并且1区的标签增多会往上顶

《模块化》只要一个文件超过150行 你就需要模块化

模块化就很简单的把一个长组件拆成几个小的组件

《构建思路》 先写html 再按照html上的class名去按照层级去书写css,

Vue中使用ts其实就是用ts写Vue组件的意思!

  • Vue组件由 class ,装饰器,以及原版js组件中就有的东西,具体用法查看装饰器的相关文档比如vue-property-decorator

Vue中使用ts其实和ts没多大关系,ts仅仅是对js的类型做出了升级

但是使用中诸如ts在Vue中如果要使用的话需要一些特殊的写法或者装饰器这类,都不算是ts的本身

  • 以下几句照抄

为什么要使用@Component会出现多个装饰器的库供选择?

  • vue-class-component是原版 Evan写的
  • 下面这个是另外一个人写的,写得更好,于是用它的,

学习思路,就是区搜关键词vue-property-decorator在github上然后CRM学习法去抄写法

github.com/kaorun343/v…

收入和支出的切换功能是如何制作的?

简单的代码也要考虑健壮性,即做判断然后报错不让程序执行下去

当你用webstorm直接修改package.json的时候,webstorm会自动提醒你要不要更新

项目中的问题和知识是项目中的问题和知识 与你的基础知识没什么大的关系

ts的好处

  1. 是在webstorm中使用会有大量代码提示,比如你用ts写的子组件中声明了一个prop,那么当你在父组件中使用的时候就会告诉你,有哪些prop可以传递
  2. 编写代码的时候就会告诉你有哪些问题,而不用非等到编译的时候来一大堆报错,比如你的方法名写错了,或者根本没有这个方法

分清楚几种提示的来源,webstorm的弱提示,ts的编译时提示,浏览器的运行时提示

《面试》ts和js有哪些区别

ts的本质就是在js的后面跟个冒号写一些类型,通过tsc去检查数据有无错误,然后tsc会把ts翻译成js 避免低级 bug 产生 相信大家都遇到在编辑器一顿操作,打开浏览器页面空白的尴尬翻车现场,然后一顿 debug 最后发现是把变量名拼错了, 用上 ts 之后再也不会有这样的烦恼了,类似的错误编辑器立马就提示你了: 1.2 后期项目维护成本 类型即注释 工作中难免会需要我们对某个模块升级或者修改等,有时候没有注释或者注释不全的话就需要我们去读代码了,其实很多时候我们并不关心某个模块内部的具体实现,我们只想知道他的输入输出,这时候 ts 就很适合这个场景了:

可以看到需要将对象字面量的写法转换为基于类的组件定义方式,这样很容易就可以对组件的 state 、 prop 、 method 等添加类型,同时其利用装饰器在运行时导出了标准的 vue 组件, 相较于 Vue@2.x 中基于类的组件定义方式,Vue@3.x 不需要我们改变什么代码习惯就可轻松接入 ts,并且其类型推导也是相当的强大。 最后如果非要找出个不足的话,Vue 的模板写法还是不能与 ts 兼容,虽说还有 tsx 的备选方案,但是用 tsx 就不太 Vue 了,不过相信后续也会有优秀的工具来弥补这个不足。总而言之,Vue 对 ts 的支持已足够强大,赶紧用起来吧,真香!

从冒号开始才是ts新加的语法

Vue2.X享受不到ts的好处的,Vue3才可以

Ts编译时与运行时

 @Prop(Number) xxx: number | undefined

这是Vue2中配合装饰器写的ts语句,冒号后面是编译时的类型定义,前面的Number是运行时在浏览器这端的报错

typescript的工作原理

安装typescript的时候会安装一个tsc也就是ts的compiler编译器,这个编译器会做类型检查的工作

单文件组件的三种书写方式,第二种才是ts能用上的地方

  • js对象// 最经典的写法就是这个
  • ts类 // 你要用ts就要ts+js的类组件写法
  • js类 // 作用是同js对象的,
  • 注意,只要是类的单文件组件写法,就需要引入第三方组件库vue-property-decorator,将类转成组件

ts类型有哪些

  • js基本的七种数据类型
  • 字符串数组 stringArr: string[] =[]
  • 自定义的类型,比如一个对象中有四个属性tag,notes,type,amount,我们把这个对象定义成一个类型
  • 还可以写类名也就是构造函数名,比如Date

Vue模板中如何书写保底值

<div class="output">{{output||'1'}}</div>

Vue模板中是不支持ts,只支持js的

Vue中每个函数都会有一个event对象,哪怕你根本没有设置参数

    <div class="buttons" @click="inputContent">

ts中点击事件的类型是鼠标事件对象,同理还有键盘事件对象等等

存的数据要用字符串,因为有.

计算器按钮的逻辑

  • 0后面跟任何数字都是清除0保留后一个数字,比如00 ===> 0 ; 01 ===> 1
  • 两个.是不允许出现的

清除功能用的是substr, substr这个api怎么使用的你要很熟练

  • 此api包括头不包括尾
  • substring包括头不包括尾,第二个参数可以理解为是长度
  • 参数不要去写负数!
  • 不会对原字符串修改,要复制新的一份
let str='123'
str.substring(0,str.length) === str
str.substring(0,str.length-1) === '12'

Vue数据调试大法,{{value}}写在一个页面有所展示的位置

 <label class="notes">
      {{value}}
      <span class="name">备注</span>
      <input type="text" placeholder="在这里输入备注" v-model="value">
    </label>

Vue中class的最推荐写法 不要用短路逻辑或者三元表达式,而是用对象写法,可读性更高

  • 对象语法是className : 逻辑判断
:class="{selected:selectedTags.indexOf(tag)!==-1}" // 对象语法 这是推荐的,下面的不要看了
:class="selectedTags.indexOf(tag)!==-1?'selected':' '" // 三元表达式
:class="selectedTags.indexOf(tag)!==-1&&
'selected'" // 短路逻辑

v-for制作标签 点击标签以后会呈现selected状态的制作逻辑

  • 数组
  • 没在里面就加里面
  • 在里面了就把它删掉
  • 这样才能实现点一下选中,再点一下就删除!

子组件不要去修改外部传来的prop属性的内容!,而是去.sync操作

tags组件中的标签被选中时就通知父组件

  • 父组件身上有一个数据,比如选没选中之类的,然后对这个数据.sync修饰符

watch相较于this.$emit的好处是,缓存机制,也就是比较得出不同结果才触发!

父子之间数据流向有两种,

  • 一种是父有初始值,然后prop给子,然后子通过自定义事件把父的初始值改了以后传回来更新
  • 一种是父没有初始值,子本地有个初始值,然后本地监听数据,然后传给父更新

存储数据时发现引用问题,用深拷贝思路解决 也就是序列化反序列化

  • 理论依据是,对象被保存时是地址,但是你先把对象变成字符串这就成了字符串了,再通过字符串去反序列化也就得到一个新的对象了

ts在webstorm中的使用,ctrl+鼠标左键移上去可以看到变量的type

version版本的字符串比较,比较字典顺序

var a = "1.2.2a";
var b = "1.2.2b";

console.log(a > b); // 输出true
console.log(a < b); // 输出false

a = "1.02.1";
b = "1.1";

console.log(a > b); // 输出false
console.log(a < b); // 输出true

webstorm中打开ts的提示

custom.d.ts是ts的全局声明文件 custom文件名并不重要 可以叫别的

  • 用于声明一些自定义的类型
  • 但是你声明的自定义类型不要和ts自有的类型名重复,避免冲突

ts强制类型声明 as

css能不用margin就用padding 避免边距重合

《面试》 曾将对于创建成功要返回什么有

  • 原本是列了一个列表记录数字对应的错误种类是什么
  • 项目过了一年之后因为文档更新不及时,久而久之这样的方式会弄不清的
  • 最后改成了返回字符串,只要字符串内把错误原因描述清楚即可

展示和创建标签这一块不太懂

route是用于获取路由信息的route是用于获取路由信息的 router是用于路由的跳转的

this.router.replace是可以回退的,this.router.replace是可以回退的 ,this.router.push无法回退

Vue中的.native修饰符,作用是子组件内部触发了一个原生事件之后,父组件模板中的原生事件也会触发一遍

封装共用组件 button 用到了 slot 意义在于 样式可以复用

上边栏的垂直居中, 且只有左和中两个元素,因此我们就伪造一个右边元素然后使用flex

能不用v-model尽量别用v-model 而是用它的展开形式:value + @input

讲解一下@Prop装饰器的用法

@Prop(Number) xxx: number | undefined; 
// 大写的Number 是用于运行时检查js的类型,也就是说如果父模板中给子xxx='hi' 则浏览器控制台中就会有报错
// 小写的number 是用于编译时检查ts的类型,比如this.xxx='hi',则编辑器中就会下划线提示这句类型是错误的

如果你不给ts变量一个初始值则会报错

以下的下划线就是报错,说的是你没给他初始值 解决方案1 你给prop一个|undefined 这样就算有一个保底值也就是初始值,但这种写法在后续代码有很多地方都要因为undefined的原因而报错,因此不是那么推荐 解决方案2 @Prop(Number) xxx!: number 断言这不是undefined 也不是null即可

错误的写法 以下会在父模板中给子组件传值时报错,报错信息是不要直接操作prop的值,以下就相当于给prop赋值为0,不乱传来的是什么

readonly 表示只读,但是无法控制数组等复杂类型,因为复杂类型是否被更改看的是地址

@Prop() readonly dataSource: string[];

《ts》 as断言,有时候我们会遇到ts太过扰人的提示,于是直接强制告诉ts这一定是个按钮而不会是ts认为的null即可

const button=e.target as HTMLButtonElement; 
// 强制告诉typescript e.target是一个button元素,为了解决ts的报错,

《ts》 声明的时候selectedTags: string[] = []; 而不要selectedTags = [];

《ts》 非空断言 !表示告诉ts不可能是null也不可能是undefined this.datasource.push(name!)

@Prop里面还是要写清楚类型的比如Number

@Prop中允许接收默认参数,这样一来避免父不给子传prop的情况

 @Prop({default: '-'}) type!: string; //  减号支出,加号收入

@Prop中允许写 require:true 表示这个prop是必须要传的

@Prop({required:true})

把你自定义的类型都写在custom.d.ts的文件中 d === declaration 声明

? 的意思是当你初始化一个MyRecord实例时,你可以不给notes值

// 声明一个类型
type MyRecord = {
  tags: string[];
  notes?: string;
  type: string;
  amount: number;
}
record: MyRecord = {
    tags: [], type: '-', amount: 0
  };

所有标签这个数据保存在一个数据库,记录列表则保存在另一个数据库

组件拆分,分别完成html+css,然后组件本地数据绑定,然后整体数据流向打通

内联元素比如button元素的水平居中是写在父元素身上的 text-align:center

throw new Error 与 try catch一起用的

联合类型有助于解决书写代码时的拼写错误问题

type TagListModel={
  data:string[],
  fetch:()=>string[],
  save:()=>void,
  create:(name: string)=>'success'|'duplicated' // 联合类型 说明了 只能返回这两种结果 有助于解决拼写问题
}

filter 和map的结果都是一个数组

this.$router.replace('/404') 重定向到404页面,replace而不是push为了防止用户回退不了

组件内 或者页面内的数据实时更新 只和你本地数据有关 ,如果数据没有及时更新,肯定是本地数据有问题

《bug》压根没在money却也会执行money的脚本

使用model时产生一个问题,所有页面只调用一次tagListModel.fetch()即可而不要多次调用,不然根本就产生了不同的对象

let obj={
	a:{},
    fn(n){
        this.a={age:n}
        return this.a
    }
}
let aaa=obj.fn(111) 
console.log(aaa.age) // 111
let bbb=obj.fn(222)
console.log(bbb.age) // 222
console.log(aaa.age) // 111

这个例子告诉我们,你以为返回的是同一个对象,其实根本是不同对象。

在ts中如果要使用window.xxx则需要封装Window接口

interface Window {  // window.tagList = tagListModel.fetch() 不报错
  tagList: Tag[]
  createTag:(name:string)=>void
  removeTag:(id:string)=>boolean
  updateTag:(id:string,name:string)=>'success' | 'duplicated' | 'not found'
  findTag:(id:string)=>Tag
  recordList:MyRecord[]
}

一切接口都是围绕着增删改查 增就是create创建 删就是remove 改就是update 查就是fetch获取

如果把store也就是数据封装的仓库放到window上有两个问题,全局变量容易修改;nodejs是没有window的

在一个项目中你import 一个模块一万次 都是只会引入一次,

使用了store就不再使用model了,两者是同样的职能

写在Vue类组件 介于srcipt 与 class之间的js代码是一定会执行的,哪怕不运行到这个页面也会执行

《面试》 对最新版的es有什么了解吗 可选链语法 目前处于stage3 也就是 xxx?xxx='123' 也就是有的话才执行

能不用.sync也别用.sync 因为这个是对本地数据的操作,只有当你.sync展开成@update:propName + :propName='xxx' 时,你才能对数据调用接口

有了store就可以直接拿 而不用一层层分发数据

全局状态管理(也叫全局数据管理)的好处是什么?

  • 解耦:将所有数据相关的逻辑放入 store(也就是 MVC 中的 Model,换了个名字而已)
  • 数据读写更方便:任何组件不管在哪里,都可以直接读写数据,而不用再依赖父组件传数据
  • 控制力更强:组件对数据的读写只能使用 store 提供的 API 进行(当然也不排除有猪队友直接对 tagList 和 recordList 进行 push 等操作,这是没有办法禁止的)

computed通常与Vuex中的state数据一起用的,Vuex这个中央数据管理器中的数据一旦变化也会通知到组件

Vue.use的作用是将store挂载到Vue.prototype上,这样一来就允许你this.$store这样调用,不用再在组件中导入store了

vue-class-component 中如果要使用mixins需要看官方提供的文档照做

class-component.vuejs.org/guide/exten…

this.$store.commit 是不能return值出去的,一切都要依靠state去获取!

不推荐使用第三方库vue-property-decorator中的computed,而是推荐使用官方库的

mutations注意事项

  • 1.mutations中的函数是不能够return一个值到外部的,
  • 2.mutation中的函数只能接收两个参数,第一个是state,第二个是payload,payload也就是一个对象或者别的都可以

actions是在异步的时候用的

如何父给子组件传递css,以下是最佳实践

先上结论,classPrefix + Vue-deep 两者结合效果好 这里是阿里团队的css穿透法,核心思想是传递一套皮肤,这一套皮肤是class-prefix的名字

// 父组件中调用子组件Layout时传递一个propName叫class-prefix,名字随便起
  <Layout class-prefix="layout">  </Layout>
  • 并且需要在父组件中新建一个全局style,当然也可以在别处新建全局style,

子组件如下书写即可 总结一下,父给子一个global的css这个都能想到,难点在于如果直接传递过去则class仅仅会存在于子组件的最外层,而如果以上述方式,则可以实现传衣服->穿衣服,传裤子->穿裤子

vue deep 深度选择器 可以穿透 子组件

制作tab的,点击选中的制作思路总结,用脑子复现一下

template中起名要注意,尽量多用data-source value这种普遍的名字,但是变量名你可以起得特殊,好处是易读易理解,已修改成组件方便复用

    <Tabs :data-source="typeArray" :value.sync="tabValue"/>

如何解决样式覆盖的问题?

先上结论,不要轻易用important; selector写得越详细则优先级越高

  • 思路很简单,当父组件用deep想要修改子组件的样式的时候,常常遇到样式无法覆盖,则考虑优先级
  • 利用selector数越多 优先级越高的特点

《css选择器》要搞清楚 a b 是后代 ; ab是两个兼在

析构的语法在Vue中也经常用到

const {recordList}=this;
const recordList=this.recordList;

前端对于时间的处理

  • ISO 8601 是一种日期时间表示法 以T为分隔符
let a = new Date() // Date对象
console.log(a) 
a.toISOString() // 转换成一个ISO字符串 "2020-12-20T04:55:20.889Z"
// 这个ISO字符串是零时区的标准时间,而不是中国时间

// 以下是得到当前时间总秒数,有两种方式
let b=Date.parse(a) // 1608440120000 a是一个Date对象
let b=a.getTime()  // 1608440120000 a是一个Date对象

// Date.parse可以接收字符串也可以接收Date对象
Date.parse('2020-12-17') // 得到秒数
Date.parse(日期对象) // 得到秒数


// 如果你已有一个秒数,想要变成一个Date对象
let ccc=new Date(1608440120000)

// Date对象身上有各种api获取当前时区的时间

我们用day.js更好的优化以上一系列api

statistics页面的思路