Pseudo

Pseudo 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

Pseudo 赞了文章 · 11月6日

思否独立开发者丨@羊二:写代码里单人骑行最远的,骑行里面最会写代码的

独立项目名称:中国古典颜色在线手册

思否社区ID:@zerosoul


今天我们采访的独立开发者是@羊二,他现在在北京一家新创小公司做前端技术经理,平时工作偏管理,偶尔也会参与写代码。出于技术的热爱,有时会捣腾一些自己感兴趣的小东西,来实践自己的脑洞。他的独立开发作品大多精美有趣。

下面我们就通过他的人生经历来深度了解他。

大学期间,一次偶然的机会,他参加了全国大学生数学建模竞赛。三人一组,参赛人员主要来自数学系和计科系,据他说自己学校不那么有名,也没拿过国家一等奖,听说以前有人拿过国二就让全校沸腾了。

他作为小组的leader,亲自选将,挑了两个玩的很好的朋友报名参加了,准备了大约一个月,决赛奋斗了三天三夜,整整三天三夜几乎都没怎么睡觉!十一月份公布结果时,竟然是国家一等奖!虽然让数学系很丢面子,但给计科系和学校争得了荣誉。

通过这次经历他意识到:团队竞技,能否做到沟通及时,默契配合是很重要的!

大学毕业后,他完成了一场非常有纪念意思的旅行:

一个人,一辆单车,一路借宿,从南方骑行到北方,2500+km,一个多月中,看过的风景,经历的事情,认识的人,数不胜数。

在南方,爬过山,趟过河,穿过村庄,眼里看到的是山清水秀,耳里听到的是蛙鸣鸟叫,闻的是花香,喝的是泉水,体验的是淳朴的民风。也曾迷茫过,也曾无助过,但车轮已经顾及不上坡多路险,身体已然适应了阴雨连绵。进过社区的民宅,到过民工的帐篷,去过学生的宿舍,投宿过路旁目不识丁的农户家,也被盛情邀请过到依山傍水的村民家借宿。绵延无尽的路,遇到了不少有故事的人,发生了许多起起伏伏的事,奇怪的是,我脑袋里始终没有想过“成熟者”们津津乐道的“人心险恶”。

奔向北方,旅途过半,稍显疲惫,但还是毅然决然地一路北上。一路上,与军区首长喝过酒,与无家可归的流浪汉握过手,与现实版的“硬汉”同过行,与目不识丁的放羊大叔聊过天,与在外打工的东北彪汉谈过人生,与出来流浪寻找美食的美女逛过街。当时的自己就像一个永不断电的“发动机”,头顶的太阳提供能量,当然,顺带也帮自己完成了一次“蜕变”:晒脱了一层皮。就这样,每一天都有不同的人走进我的人生,又匆匆离开,虽说只是几小时的朋友,却成了我一辈子的回忆。

我有一件纯白色的文化衫,每到一处借宿,都厚颜无耻地向借宿者要签名,有老人,有小孩,有警察,有教师,有高官,有平民,有路人甲,也有路人乙,直到本来好好的一件衣服,被五颜六色七拐八拐的笔画占据得满满的。

说到最近的计划,@羊二表示明年计划结婚,最近在想着结合婚礼做一个辅助性的工具网站,用来辅助整个结婚流程:婚礼邀请,预算清单,待做列表,宾客安排,现场弹幕等。如果想法都能实现,并经过自己的婚礼检验,或许,会成为自己的第一个具有商业价值的产品😀。

不过@羊二并没有把自己看成一个严格意义的独立开发者,在他心中独立开发者要有面向市场且具有商业价值的产品,他也在努力让自己向这个方向前进。

中国古典色在线手册

项目介绍:中国古典色在线手册

立项日期:2019-08

项目背景:喜欢,想做,就做了。

面向群体:设计师、审美爱好者

1、如何做的第一版产品?

中国色的灵感来源于:http://nipponcolors.com一个日本设计师小哥做的日本色站点。我想为何不来个中国色版本呢?

2、独立开发过程中遇到过哪些困难?项目为你带来了什么?

主要的困难是如何保持代码的简洁,功能的克制(不乱加功能)。出于兴趣做的,代码开源,目前只带来了 Github Star 近1.6k。

3、你的商业模式是什么?如果项目重来一次你会做哪些改变?

没商业模式,就是做好之后,去V站做了一次宣传,然后莫名其妙就火了,带来了一定知名度

如果有可能改变的话,我会想做小程序。

6、你怎么看待开源

我的作品大多数都开源了,这对程序员来说,是很正常的。看到别人fork我的代码感觉挺开心,就像一把火点燃了另一把火。以前我做过一个在线呼吸的webapp(https://works.yangerxiao.com/breathe-relaxer/),通过视觉反馈工具,来控制呼、吸、屏气,进而达到放松身心目的。后来有人联系我说想应用到他的论文中,太奇妙了,当初做这个小东西,怎么也想不到会和在世界某地的人写的论文产生关联。还有一个,前段时间有个人在我的中国色项目里留言(https://github.com/zerosoul/chinese-colors/issues/7#issuecomment-688176130),说基于我的web app项目,做了一个小程序版本,体验了下,UX还原度很高,很赞。一把火点燃另一把火,这种经历大概是我开源代码的动力之一。

个人相关问题

1、推荐你最喜欢的一款产品 / 游戏 / App?并说明原因

Twitter 原因:PWA体验做的非常好(很奇怪的关注点😀)

2、分享一下你的技术栈和你日常的工作流?

React/Gatsby.js/Graphql

3、对独立开发者或编程初学者有什么建议?

阅读官方文档,阅读官方文档,阅读官方文档。(重要的事情说三遍)。另:学好英语,养成追溯一手资料的习惯;使用google、stackoverflow。

4、生活中有什么爱好?有什么个人的特别的工作习惯么?

曾经(2012年)单人单车从贵阳骑到北京,一路借宿,骑了一个月。可能是会写代码里单人骑行最远的,骑行里面最会写代码的😂。我觉得自己没啥特别的工作习惯,如果一定要说一个的话:思(tuo)考(lan)。写代码之前,我会做很长时间的思考,代码的输出可能只是其中很短的一环。当然,这不一定是个好习惯:稍不留神,就发展成为拖延症

开发者寄语

我的个人站点(最近想改版):https://yangerxiao.com/ 从这里对我可以做更多的了解哈~


独立开发者支持计划-1.png

该内容栏目为「SFIDSP - 思否独立开发者支持计划」。为助力独立开发者营造更好的行业环境, SegmentFault 思否社区作为服务于开发者的技术社区,正式推出「思否独立开发者支持计划」,我们希望借助社区的资源为独立开发者提供相应的个人品牌、独立项目的曝光推介。

有意向的独立开发者或者独立项目负责人,可通过邮箱提供相应的信息(个人简介、独立项目简介、联系方式等),以便提升交流的效率。

联系邮箱:pr@segmentfault.com


image

二维码过期添加思否小姐姐拉你入群
image.png
查看原文

赞 9 收藏 1 评论 0

Pseudo 关注了问题 · 10月28日

nuxt 怎么使用mixins方法的混入

在网上没有看到引入的方法,就本地大概配置了一下

在plugins中新创建了一个js文件【如图】,然后在nuxt.config引入 但是页面会执行多次?请问有正确的引入方式吗?
image.png

关注 3 回答 1

Pseudo 赞了文章 · 10月22日

Flex布局完全教程

原文:A Complete Guide to Flexbox
作者:CHRIS COYIER
译者:Shelley Lee
本文同时发布于知乎专栏:前端指南
转载需提前联系译者,未经允许不得转载。

背景介绍

Flexbox 布局(也叫Flex布局,弹性盒子布局)模块目标在于提供一个更有效地布局、对齐方式,并且能够使父元素在子元素的大小未知或动态变化情况下仍然能够分配好子元素之间的间隙。

Flex布局的主要思想是使父元素能够调节子元素的高度、宽度和排布的顺序,从而能够最好地适应可用布局空间(能够适应不同的设备和不同大小的屏幕)。设定为flex布局的父元素(容器)能够放大子元素使之尽可能填充可用空间,也可以收缩子元素使之不溢出。

最重要的是,与传统布局中块状元素按照垂直方向摆放,行内元素按照水平方向摆放相比,flex布局是无方向的。传统布局在应对大型复杂的布局时缺乏灵活性,特别是在改变方向、改变大小、伸展、收缩等等方面。

: Flex 布局比较适合小规模的布局,Gird布局面向更大规模的布局。

基本概念

Flex布局是一个完整的模块而不是一个单独的属性,它包括了完整的一套属性。其中有的属性是设置在容器(container,也可以叫做父元素,称为flex container)上,有的则是设置在容器的项目上(item,也可以叫做子元素,称为flex items)上。


译者注:由于item译成项目不够直观和形象,以下统一用父元素指代container,子元素指代item。

如果我们可以说传统布局是建立在块状元素垂直流和行内元素水平流上的,那么flex布局就是建立在"flex-flow方向"上的,通过下图解释flex布局的主要思想。

在flex布局中,子元素要么按照主轴也就是main axis(从main-startmain-end)排布,要么按照交叉轴,也就是cross axis(从cross-startcross-end)排布。

下面介绍几个概念:

  • __main axis__: Flex 父元素的主轴是指子元素布局的主要方向轴,注意主轴不一定是水平的,它由属性flex-direction来确定主轴是水平还是垂直的(后面会介绍)。

  • __main-start|main-end__: 分别表示主轴的开始和结束,子元素在父元素中会沿着主轴从main-startmain-end排布。

  • __main size__: 单个项目占据主轴的长度大小。

  • __cross axis__: 交叉轴,与主轴垂直。

  • __cross-start|cross-end__: 分别表示交叉轴的开始和结束。子元素在交叉轴的排布从cross-start开始到cross-end

  • __cross size__: 子元素在交叉轴方向上的大小。

属性介绍

属性分作用于父元素的属性和作用于子元素的属性两部分介绍。

父元素属性

display

用来定义父元素是一个 flex布局容器。如果设置为flex则父元素为块状元素,设置为inline-flex父元素呈现为行内元素。

.container {
  display: flex; /* or inline-flex */
}

flex-direction

flex-direction定义flex布局的主轴方向。flex布局是单方向布局,子元素主要沿着水平行或者垂直列布局。

.container {
  flex-direction: row | row-reverse | column | column-reverse;
}
  • row: 行方向,flex-direction的默认值,在ltr(left to right, 从左到右)排版方式下从左到右排列,在rtl(right to left, 从右到左)排版方式下从右到左排列。

  • row-reverse: 行反方向,在ltr中从右向左,在rtl中从左到右。

  • column: 列方向,与row相似,只是从上到下。

  • column-reverse: 列反方向,与row-reverse相似,只是从下岛上。

flex-wrap

默认情况下,flex布局中父元素会把子元素尽可能地排在同一行,通过设置flex-wrap来决定是否允许子元素这行排列。

.container{
  flex-wrap: nowrap | wrap | wrap-reverse;
}
  • nowrap: 不折行,默认值,所有的子元素会排在一行。

  • wrap: 折行,子元素会从上到下根据需求折成多行。

  • wrap-reverse: 从下向上折行,子元素会从下岛上根据需求折成多行。

这里有一些可视化的示例

flex-flow

flex-flowflex-directionflex-wrap属性的缩写形式。默认值是row,nowrap

flex-flow: <‘flex-direction’> || <‘flex-wrap’>

justify-content

justify-content属性定义了子元素沿主轴方向的对齐方式,用来当子元素大小最大的时候,分配主轴上的剩余空间。也可以当子元素超出主轴的时候用来控制子元素的对齐方式。

  • flex-start: 默认值,朝主轴开始的方向对齐。

  • flex-end: 朝主轴结束的方向对齐。

  • center: 沿主轴方向居中。

  • space-between: 沿主轴两端对齐,第一个子元素在主轴起点,最后一个子元素在主轴终点。

  • space-around: 沿主轴子元素之间均匀分布。要注意的是子元素看起来间隙是不均匀的,第一个子元素和最后一个子元素离父元素的边缘有一个单位的间隙,但两个子元素之间有两个单位的间隙,因为每个子元素的两侧都有一个单位的间隙。

.container {
  justify-content: flex-start | flex-end | center | space-between | space-around;
}

align-items

align-items定义了子元素在交叉轴方向的对齐方向,这是在每个子元素仍然在其原来所在行的基础上所说的。可以看作是交叉轴上的justify-content属性;

.container {
  align-items: flex-start | flex-end | center | baseline | stretch;
}
  • flex-start: 按照交叉轴的起点对齐。

  • flex-end: 按照交叉轴的终点对齐。

  • center: 沿交叉轴方向居中。

  • baseline: 按照项目的第一行文字的基线对齐。

  • stretch: 默认值,在满足子项目所设置的min-heightmax-heightheight的情况下拉伸子元素使之填充整个父元素。

align-content

align-content是当父元素所包含的行在交叉轴方向有空余部分时如何分配空间。与justify-content在主轴上如何对单个子元素对齐很相似。

注意:当只有一行的时候,该属性并不起作用。

.container {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}

译者注:该属性中的六个属性值与justify-content中的六个属性意思相似,不同之处在于justify-content沿主轴方向的作用于单个子元素,而align-content沿交叉轴方向作用于行。遂不再赘述各属性值含义。

译者注:注意align-itemsalign-content的区别,前者是指在单行内的子元素对齐方式,后者是指多行之间的对齐方式。

父元素属性总结

  display: flex|inline-flex;
  flex-direction: row | row-reverse | column | column-reverse;
  flex-wrap: nowrap | wrap | wrap-reverse;
  flex-flow: <‘flex-direction’> || <‘flex-wrap’>;
  justify-content: flex-start | flex-end | center | space-between | space-around;
  align-items: flex-start | flex-end | center | baseline | stretch;
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;

子元素属性

order

默认情况下,子元素按照代码书写的先后顺序布局,但order属性可以更改子元素出现的顺序。

.item {
  order: <integer>;
}

译者注order的默认值为0;子元素的order值越小,布局越排在前面,参考例图理解。

flex-grow

flex-grow规定在空间允许的情况下,子元素如何按照比例分配可用剩余空间。如果所有的子元素的属性都设定为1,则父元素中的剩余空间会等分给所有子元素。如果其中某个子元素的flex-grow设定为2,则在分配剩余空间时该子元素将获得其他元素二倍的空间(至少会尽力获得)。

.item {
  flex-grow: <number>; /* default 0 */
}

flex-grow不接受负值。

译者注:默认值为0,意味着即使有剩余空间,各子元素也不会放大。

flex-shrink

flex-grow属性类似,flex-shrink定义了空间不足时项目的缩小比例。

.item {
  flex-shrink: <number>; /* default 1 */
}

flex-shrink不接受负值。

译者注flex-shrink默认值为1, 当所有子元素都为默认值时,则空间不足时子元素会同比例缩小。如果其中某个子元素的flex-shrink值为0,则空间不足时该子元素并不会缩小。如果其中某个子元素的flex-shrink值为2时,则空间不足时该子元素会以二倍速度缩小。

flex-basis

flex-basis定义了在计算剩余空间之前子元素默认的大小。可以设置为某个长度(e.g. 20%, 5rem, etc.)或者关键字。关键字auto意味着子元素会按照其本来的大小显示。关键字content意味着根据内容来确定大小——这个关键字到目前没有被很好地支持,所以测试起来比较困难,与content的类似的关键字还有max-content, min-content, fit-content

.item {
  flex-basis: <length> | auto; /* default auto */
}

如果设置为0, 则子元素内容周围的空隙不会根据flex-grow按比例分配,如果设置为auto,则子元素周围额外的空袭会根据flex-grow按照比例分配,如下图:

flex

flexflex-growflex-shrinkflex-basis三个属性的缩写。其中第二个和第三个参数(flex-grow,flex-basis)是可选的。默认值为0 1 auto

.item {
  flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}

推荐使用缩写形式而不是单独地设置每一个属性,缩写形式中会智能地计算出相关值。

align-self

通过设置某个子元素的align-self属性,可以覆盖align-items所设置的对齐方式。属性值与align-items中的意义相同,不再赘述。

.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

float,clearvertical-align对flex子元素没有任何影响。

示例

示例一:水平垂直居中

我们从一个非常非常简单的例子开始,解决一个我们经常会遇到的问题:水平垂直居中。如果使用flex布局会非常简单。

.parent {
  display: flex;
  height: 300px; /* 随意设定大小 */
}

.child {
  width: 100px;  /* 随意设定大小,比父元素要小 */
  height: 100px; /* 同上 */
  margin: auto;  /* 见证奇迹的时刻 */
}

这个主要原因是,在flex布局的父元素中设置marginauto会自动吸收额外的空间,所以设置水平垂直的margin都为auto会使子元素在水平垂直方向上都完美居中。

示例二:响应式初体验

现在我们考虑用更多的属性。考虑有6个子元素,有固定的大小,但是我们希望他们能够在改变浏览器宽度的时候仍然可以在水平轴上完美地显示(注意在不使用媒体查询的前提下)。

.flex-container {
  /* 首先我们先创建一个flex布局上下文 */
  display: flex;
  
  /* 然后我们定义flex方向和是否允许子元素换行
   * 注意这与以下代码等价:
   * flex-direction: row;
   * flex-wrap: wrap;
   */
  flex-flow: row wrap;
  
  /* 然后我们定义在剩余空间上子元素如何排布 */
  justify-content: space-around;
}

完成。剩下的就是一些其他样式如颜色的设置了。

在线示例

改变浏览器大小,看看布局会有什么变化吧!

示例三:响应式导航栏

让我们再尝试一些别的东西。假设我们有一个向右对齐的导航栏在我们网页的最上端,但是我们希望它在中屏上显示时为居中,在小屏上显示为单列。同样使用flex布局,实现起来会很简单。

/* 大屏 */
.navigation {
  display: flex;
  flex-flow: row wrap;
  /* 这里设置对齐主轴方向的末端 */
  justify-content: flex-end;
}

/* 中屏 */
@media all and (max-width: 800px) {
  .navigation {
    /* 当在中屏上,设置居中,并设置剩余空间环绕在子元素左右 */
    justify-content: space-around;
  }
}

/* 小屏 */
@media all and (max-width: 500px) {
  .navigation {
    /* 在小屏上,我们不在使用行作为主轴,而以列为主轴 */
    flex-direction: column;
  }
}

在线示例

改变浏览器大小,看看布局会有什么变化吧!

示例四:移动优先的三栏布局

我们通过灵活使用flex布局尝试一些更好玩的布局。来做一个移动优先的3列布局并带有全屏宽的header和footer。

.wrapper {
  display: flex;
  flex-flow: row wrap;
}

/* 我们要告诉所有的子元素宽度 100% */
.header, .main, .nav, .aside, .footer {
  flex: 1 100%;
}

/* 移动优先依赖于源代码默认的渲染顺序
 * in this case:
 * 1. header
 * 2. nav
 * 3. main
 * 4. aside
 * 5. footer
 */

/* 中屏 */
@media all and (min-width: 600px) {
  /* 我们要告诉两边的sidebar共享一个行 */
  .aside { flex: 1 auto; }
}

/* 大屏幕 */
@media all and (min-width: 800px) {
  /* 通过order设定各个面板的渲染顺序
   * 告诉主要面板元素占用侧栏两倍的空间
   */
  .main { flex: 2 0px; }
  
  .aside-1 { order: 1; }
  .main    { order: 2; }
  .aside-2 { order: 3; }
  .footer  { order: 4; }
}

在线示例

改变浏览器大小,看看布局会有什么变化吧!

浏览器前缀

Flex布局需要一些浏览器前缀来最大力度地兼容大多数的浏览器。Flex布局的前缀不只是在属性前面添加浏览器前缀,不同浏览器下的属性名和属性值都不同,这是因为Flexbox布局的标准一直在变,一共有old, tweener, new 三个版本。

可能处理前缀的最好方法是使用新的语法书写CSS并通过Autoprefixer运行CSS,能够很好地处理这个问题。

另外,这里有一个Sass中 @mixin 来处理一些前缀,也可以给你一些处理前缀的启发:

@mixin flexbox() {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
}

@mixin flex($values) {
  -webkit-box-flex: $values;
  -moz-box-flex:  $values;
  -webkit-flex:  $values;
  -ms-flex:  $values;
  flex:  $values;
}

@mixin order($val) {
  -webkit-box-ordinal-group: $val;  
  -moz-box-ordinal-group: $val;     
  -ms-flex-order: $val;     
  -webkit-order: $val;  
  order: $val;
}

.wrapper {
  @include flexbox();
}

.item {
  @include flex(1 200px);
  @include order(2);
}

其他资源

Bugs

我见过的最棒的flexbox bug总结是Philip Walton 和 Greg Whitworth的Flexbugs,是开源的,你可以在上面跟踪动态。

浏览器支持

首先看一下Flex布局的三个版本

  • (new)是指标准中最近的语法(e.g. display:flex;)。

  • (tweener)是指2011年以后非官方的临时版本(e.g. display:flexbox;)。

  • (old)是指2009年以后的旧语法(e.g. display:box;)

Blackberry browser 10+ 支持新语法。

更多混合使用语法达到最佳浏览器兼容,可以参考this article (CSS-Tricks)或者this article (DevOpera)

译者的话

网上有不少flex相关教程,但当我看到CHRIS COYIER的这篇文章时,不禁被其详尽所震撼,最近也在撰写布局相关的文章,故产生了翻译此文的想法。翻译过程中尽量保持原文原貌,部分地方做了小幅调整以便更加符合中文思维。文中图片均来源于原文。水平有限,如有误漏之处,还请读者不吝赐教。最后希望此文能给读者带去帮助。

更多讨论请到180251611

查看原文

赞 19 收藏 44 评论 4

Pseudo 收藏了文章 · 10月19日

无缝改造vue项目,支持typescript

改造 vue_cli3+js 版本支持 ts

生成vue项目的vue_cli版本为 4.0.5

应用场景:

  • 目前已经在开发的项目, 后续想要摸摸 ts
  • 刚开始学习 ts, 不敢完全入坑

安装

yarn add typescript ts-loader --dev // 编译用
yarn add vue-property-decorator // 写vue组件时用
yarn add fork-ts-checker-webpack-plugin --dev // typescript 类型检查的webpack插件
yarn add @types/webpack-env // 包含webpack的类型定义(在tsconfig.json中定义types用,目前没有测试出有什么影响)
yarn add @typescript-eslint/parser --dev // eslint中的parse依赖r

书写 vue.config.js 修改 webpackloader

vue.config.js 必须是 js 文件

const path = require("path");
function resolve(dir) {
  return path.join(__dirname, dir);
}
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

module.exports = {
    // lintOnSave: process.env.NODE_ENV === "development",
    lintOnSave: true,
    configureWebpack: {
        resolve: {
          extensions: ['.tsx','.ts', '.mjs', '.js', '.jsx', '.vue', '.json', '.wasm']
        }
    },
    chainWebpack: config => {
        // 处理ts文件 (新增loader)
        config.module
            .rule('ts')
            .test(/\.tsx?$/)
            .exclude
                .add(resolve('node_modules'))
                .end()
            .use('cache-loader')
                .loader('cache-loader')
                .options({
                    cacheDirectory: resolve('node_modules/.cache/ts-loader')
                })
                .end()
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .use('ts-loader')
                .loader('ts-loader')
                .options({
                    transpileOnly: true, // 关闭类型检查,即只进行转译(类型检查交给webpack插件(fork-ts-checker-webpack-plugin)在另一个进程中进行,这就是所谓的多进程方案,如果设置transpileOnly为false, 则编译和类型检查全部由ts-loader来做, 这就是单进程方案.显然多进程方案速度更快)
                    appendTsSuffixTo: ['\\.vue$'],
                    happyPackMode: false
                })
                .end()
        
        // eslint 自动修复 (修改已经存在的loader)
        config.module
            .rule('eslint')
            .test(/\.(vue|(j|t)sx?)$/)
            .pre() // eslint是pre处理的
            .use('eslint-loader')
                .loader('eslint-loader')
                .tap(options => { // 修改已经存在loader的配置
                    options.fix = true
                    return options
                })
                .end()
        
        // 使用webpack 插件进行typescript 的类型检查 fork-ts-checker-webpack-plugin
        config
            .plugin('fork-ts-checker')
            .use(ForkTsCheckerWebpackPlugin, [{
                vue: true,
                tslint: false,
                formatter: 'codeframe',
                checkSyntacticErrors: false,
                // 因为fork-ts-checker-webpack-plugin是在单独的进程跑的,所以它的错误或警告信息是异步回传给到webpack进程的, 这时编译报错信息只在终端显示,不会在预览的浏览器界面显示报错信息。
                // 将async设置为false后,就要求webpack等待fork-ts-checker-webpack-plugin进程返回信息, 这样会在页面显示编译报错信息。不过这样做也可能会拖慢整个webpack的转译等待时间。
                // async: false 
            }])
        
    }
}

项目根目录下新建 tsconfig.json 文件

配置编译 ts 文件规则.我们默认使用了vue-cli 生成项目时选择 typescript 版本时的 tsconfig.json

  • 自定义新增了 "noImplicitAny": false,
  • strictPropertyInitialization 这个配置是要求定义类的属性时必须初始化赋值,在"strict": true 时自动设置为 true,这非常不合理,因为我们在 vue 中属性的值经常在 created/mounted 赋值, 因此设置为 "strictPropertyInitialization": false.
  • strictNullChecks 这个配置是严格的 null 检查模式. 在"strictNullChecks": true模式下("strict": true 时自动设置为 true), nullundefined 值不包含在任何类型里, 但是我们在 vuedata 里面初始化变量时,经常会初始化为 null, 因此我们将此配置设置为 false
// strictNullChecks:true时下面一行会报错
let str: string = null
  • 上述连个配置均为 strict: true配置导致的, 如果你想简单可以将其注释掉, 因为 strict 默认为 false, 上述两个配置默认也是 false
strict: true //启用所有严格类型检查选项。
// 启用 --strict相当于启用 --noImplicitAny, --noImplicitThis, --alwaysStrict, --strictNullChecks和 --strictFunctionTypes和--strictPropertyInitialization。

最终 tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
        "strictPropertyInitialization": false,
        "strictNullChecks": false,
    "jsx": "preserve",
        "noImplicitAny": false,
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env",
      "jest"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

其他版本的 tsconfig.json, 重点在 pathstypes

{
  "compilerOptions": {
    "target": "esnext", // 编译目标语法, 可以写es5, 但是我们项目的ts-loader前经过了babel-loader
    "module": "esnext", // 
    "strict": true,
        "strictPropertyInitialization": false, // strict为true时,默认为true
        "strictNullChecks": false, // 设置null为其他类型的子类型, 效果:变量或者属性可以初始化为null
    "jsx": "preserve",
        "noImplicitAny": false, // false表示运行隐式的any类型,也就是允许不设置任何类型, 这个设置运行js文件直接改成ts文件
    "importHelpers": true,
    "moduleResolution": "node", // 和nodejs一样的node_modules机制
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "paths": { // 配合baseUrl, ts文件中import 模块路径的解析规则
      "@/*": [
        "src/*",
        "src/types/*"
      ],
      "*":[
        "node_modules/*",
        "src/types/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

以上两个版本主要是 pathstypes 不同, 你需要了解他们的作用,并在工作中设置合适的值.
第二种的其他版本运行把所有的 .d.ts 文件放到 src/types 文件夹内.

src 文件下新建一个 ts 文件

src 下新建 shims-vue.d.ts 文件, 否则会报错如下:

ERROR
      TS18003: No inputs were found in config file 'tsconfig.json'. Specified 'include' paths were '["src/**/*.ts","src/**/*.tsx","src/**/*.vue","tests/**/*.ts","tests/**/*.tsx"]' and 'exclude' paths were '["node_modules"]'.

vueCli3typescript 版本有 shims-vue.d.tsshims-tsx.d.ts 这两个文件,我们不妨把他们放到 src 下.

// shim-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}
// shims-tsx.d.ts
import Vue, { VNode } from 'vue'

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any
    }
  }
}

以上文件的原理,详见 typescriptmodule-augmentation(模块补充: 可以通过路径在文件中增补类型定义)

增加 eslint 规则

解决使用 ts 内置类型时保报错 ,例如 Partial.
安装(第一步已经安装)

yarn add @typescript-eslint/parser --dev

配置 .eslintrc.js 文件的 parser 项为 @typescript-eslint/parser

  • vue-cli(js版) 生成的 .eslintrc.js 中简单修改
module.exports = {
  root: true,
  env: {
    node: true,
        browser: true,
        es6: true
  },
  'extends': [
    'plugin:vue/essential',
    'eslint:recommended',
    '@vue/prettier'
  ],
  parserOptions: {
    // parser: 'babel-eslint',
        parser: '@typescript-eslint/parser', // 解析ts文件, 例如识别ts文件的内置类型
        ecmaFeatures: {
          legacyDecorators: true
        }
  },
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  },
  overrides: [
    {
      files: [
        '**/__tests__/*.{j,t}s?(x)',
        '**/tests/unit/**/*.spec.{j,t}s?(x)'
      ],
      env: {
        jest: true
      }
    }
  ]
}

在上述 .eslintrc.js 的配置中, 默认是使用双引号和分号结尾的, 当我在 rules中修改时,会和 prettier 的配置冲突, 因此在根目录下新建 .prettierrc.js 文件,书写

module.exports = { 
    "printWidth": 80, // 每行代码长度(默认80)
    "tabWidth": 2, // 每个tab相当于多少个空格(默认2)
    "useTabs": false, // 是否使用tab进行缩进(默认false)
    "singleQuote": true, // 使用单引号(默认false)
    "semi": false, // 声明结尾使用分号(默认true)
    "trailingComma": "all", // 多行使用拖尾逗号(默认none)
    "bracketSpacing": true, // 对象字面量的大括号间使用空格(默认true)
    "jsxBracketSameLine": false, // 多行JSX中的>放置在最后一行的结尾,而不是另起一行(默认false)
    "arrowParens": "avoid" // 只有一个参数的箭头函数的参数是否带圆括号(默认avoid)
};
  • 其他版本
module.exports = {
  root: true,
  parserOptions: {
      // +++++++++++
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    ecmaFeatures: {
      legacyDecorators: true
    }
  },
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  // plugin:包名/配置名称
  extends: ['plugin:vue/recommended', 'eslint:recommended'],

  // add your custom rules here
  //it is base on https://github.com/vuejs/eslint-config-vue
  rules: {
    "vue/max-attributes-per-line": [2, {
      "singleline": 10,
      "multiline": {
        "max": 1,
        "allowFirstLine": false
      }
    }],
    "vue/singleline-html-element-content-newline": "off",
    "vue/multiline-html-element-content-newline":"off",
    "vue/name-property-casing": ["error", "PascalCase"],
    "vue/no-v-html": "off",
    'accessor-pairs': 2,
    'arrow-spacing': [2, {
      'before': true,
      'after': true
    }],
    'block-spacing': [2, 'always'],
    'brace-style': [2, '1tbs', {
      'allowSingleLine': true
    }],
    'camelcase': [0, {
      'properties': 'always'
    }],
    'comma-dangle': [2, 'never'],
    'comma-spacing': [2, {
      'before': false,
      'after': true
    }],
    'comma-style': [2, 'last'],
    'constructor-super': 2,
    'curly': [2, 'multi-line'],
    'dot-location': [2, 'property'],
    'eol-last': 2,
    'eqeqeq': ["error", "always", {"null": "ignore"}],
    'generator-star-spacing': [2, {
      'before': true,
      'after': true
    }],
    'handle-callback-err': [2, '^(err|error)$'],
    'indent': [2, 2, {
      'SwitchCase': 1
    }],
    'jsx-quotes': [2, 'prefer-single'],
    'key-spacing': [2, {
      'beforeColon': false,
      'afterColon': true
    }],
    'keyword-spacing': [2, {
      'before': true,
      'after': true
    }],
    'new-cap': [2, {
      'newIsCap': true,
      'capIsNew': false
    }],
    'new-parens': 2,
    'no-array-constructor': 2,
    'no-caller': 2,
    'no-console': 'off',
    'no-class-assign': 2,
    'no-cond-assign': 2,
    'no-const-assign': 2,
    'no-control-regex': 0,
    'no-delete-var': 2,
    'no-dupe-args': 2,
    'no-dupe-class-members': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-empty-character-class': 2,
    'no-empty-pattern': 2,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 2,
    'no-extra-parens': [2, 'functions'],
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implied-eval': 2,
    'no-inner-declarations': [2, 'functions'],
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-iterator': 2,
    'no-label-var': 2,
    'no-labels': [2, {
      'allowLoop': false,
      'allowSwitch': false
    }],
    'no-lone-blocks': 2,
    'no-mixed-spaces-and-tabs': 2,
    'no-multi-spaces': 2,
    'no-multi-str': 2,
    'no-multiple-empty-lines': [2, {
      'max': 1
    }],
    'no-native-reassign': 2,
    'no-negated-in-lhs': 2,
    'no-new-object': 2,
    'no-new-require': 2,
    'no-new-symbol': 2,
    'no-new-wrappers': 2,
    'no-obj-calls': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-path-concat': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-regex-spaces': 2,
    'no-return-assign': [2, 'except-parens'],
    'no-self-assign': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow-restricted-names': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-this-before-super': 2,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-undef': 2,
    'no-undef-init': 2,
    'no-unexpected-multiline': 2,
    'no-unmodified-loop-condition': 2,
    'no-unneeded-ternary': [2, {
      'defaultAssignment': false
    }],
    'no-unreachable': 2,
    'no-unsafe-finally': 2,
    'no-unused-vars': [2, {
      'vars': 'all',
      'args': 'none'
    }],
    'no-useless-call': 2,
    'no-useless-computed-key': 2,
    'no-useless-constructor': 2,
    'no-useless-escape': 0,
    'no-whitespace-before-property': 2,
    'no-with': 2,
    'one-var': [2, {
      'initialized': 'never'
    }],
    'operator-linebreak': [2, 'after', {
      'overrides': {
        '?': 'before',
        ':': 'before'
      }
    }],
    'padded-blocks': [2, 'never'],
    'quotes': [2, 'single', {
      'avoidEscape': true,
      'allowTemplateLiterals': true
    }],
    'semi': [2, 'never'],
    'semi-spacing': [2, {
      'before': false,
      'after': true
    }],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, 'never'],
    'space-in-parens': [2, 'never'],
    'space-infix-ops': 2,
    'space-unary-ops': [2, {
      'words': true,
      'nonwords': false
    }],
    'spaced-comment': [2, 'always', {
      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
    }],
    'template-curly-spacing': [2, 'never'],
    'use-isnan': 2,
    'valid-typeof': 2,
    'wrap-iife': [2, 'any'],
    'yield-star-spacing': [2, 'both'],
    'yoda': [2, 'never'],
    'prefer-const': 2,
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
    'object-curly-spacing': [2, 'always', {
      objectsInObjects: false
    }],
    'array-bracket-spacing': [2, 'never']
  }
}

一个注意

如果你写了如下代码, 会报一个错误:

<template>
<div>{{message}}</div>
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator"
@Component
export default class TestTs extends Vue {
  message: string = "hello ts!";
  mounted() {
    setTimeout(()=>{
      this.message = "hello typeScript!"
    },2000)
  }
}
</script>

错误如下:

error  Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.

解决方法: 修改eslintrc.js

parserOptions: {
    ecmaFeatures: {
      legacyDecorators: true
    }
  },

vue-property-decorator 的使用

vue-property-decorator

查看原文

Pseudo 赞了文章 · 10月19日

无缝改造vue项目,支持typescript

改造 vue_cli3+js 版本支持 ts

生成vue项目的vue_cli版本为 4.0.5

应用场景:

  • 目前已经在开发的项目, 后续想要摸摸 ts
  • 刚开始学习 ts, 不敢完全入坑

安装

yarn add typescript ts-loader --dev // 编译用
yarn add vue-property-decorator // 写vue组件时用
yarn add fork-ts-checker-webpack-plugin --dev // typescript 类型检查的webpack插件
yarn add @types/webpack-env // 包含webpack的类型定义(在tsconfig.json中定义types用,目前没有测试出有什么影响)
yarn add @typescript-eslint/parser --dev // eslint中的parse依赖r

书写 vue.config.js 修改 webpackloader

vue.config.js 必须是 js 文件

const path = require("path");
function resolve(dir) {
  return path.join(__dirname, dir);
}
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

module.exports = {
    // lintOnSave: process.env.NODE_ENV === "development",
    lintOnSave: true,
    configureWebpack: {
        resolve: {
          extensions: ['.tsx','.ts', '.mjs', '.js', '.jsx', '.vue', '.json', '.wasm']
        }
    },
    chainWebpack: config => {
        // 处理ts文件 (新增loader)
        config.module
            .rule('ts')
            .test(/\.tsx?$/)
            .exclude
                .add(resolve('node_modules'))
                .end()
            .use('cache-loader')
                .loader('cache-loader')
                .options({
                    cacheDirectory: resolve('node_modules/.cache/ts-loader')
                })
                .end()
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .use('ts-loader')
                .loader('ts-loader')
                .options({
                    transpileOnly: true, // 关闭类型检查,即只进行转译(类型检查交给webpack插件(fork-ts-checker-webpack-plugin)在另一个进程中进行,这就是所谓的多进程方案,如果设置transpileOnly为false, 则编译和类型检查全部由ts-loader来做, 这就是单进程方案.显然多进程方案速度更快)
                    appendTsSuffixTo: ['\\.vue$'],
                    happyPackMode: false
                })
                .end()
        
        // eslint 自动修复 (修改已经存在的loader)
        config.module
            .rule('eslint')
            .test(/\.(vue|(j|t)sx?)$/)
            .pre() // eslint是pre处理的
            .use('eslint-loader')
                .loader('eslint-loader')
                .tap(options => { // 修改已经存在loader的配置
                    options.fix = true
                    return options
                })
                .end()
        
        // 使用webpack 插件进行typescript 的类型检查 fork-ts-checker-webpack-plugin
        config
            .plugin('fork-ts-checker')
            .use(ForkTsCheckerWebpackPlugin, [{
                vue: true,
                tslint: false,
                formatter: 'codeframe',
                checkSyntacticErrors: false,
                // 因为fork-ts-checker-webpack-plugin是在单独的进程跑的,所以它的错误或警告信息是异步回传给到webpack进程的, 这时编译报错信息只在终端显示,不会在预览的浏览器界面显示报错信息。
                // 将async设置为false后,就要求webpack等待fork-ts-checker-webpack-plugin进程返回信息, 这样会在页面显示编译报错信息。不过这样做也可能会拖慢整个webpack的转译等待时间。
                // async: false 
            }])
        
    }
}

项目根目录下新建 tsconfig.json 文件

配置编译 ts 文件规则.我们默认使用了vue-cli 生成项目时选择 typescript 版本时的 tsconfig.json

  • 自定义新增了 "noImplicitAny": false,
  • strictPropertyInitialization 这个配置是要求定义类的属性时必须初始化赋值,在"strict": true 时自动设置为 true,这非常不合理,因为我们在 vue 中属性的值经常在 created/mounted 赋值, 因此设置为 "strictPropertyInitialization": false.
  • strictNullChecks 这个配置是严格的 null 检查模式. 在"strictNullChecks": true模式下("strict": true 时自动设置为 true), nullundefined 值不包含在任何类型里, 但是我们在 vuedata 里面初始化变量时,经常会初始化为 null, 因此我们将此配置设置为 false
// strictNullChecks:true时下面一行会报错
let str: string = null
  • 上述连个配置均为 strict: true配置导致的, 如果你想简单可以将其注释掉, 因为 strict 默认为 false, 上述两个配置默认也是 false
strict: true //启用所有严格类型检查选项。
// 启用 --strict相当于启用 --noImplicitAny, --noImplicitThis, --alwaysStrict, --strictNullChecks和 --strictFunctionTypes和--strictPropertyInitialization。

最终 tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
        "strictPropertyInitialization": false,
        "strictNullChecks": false,
    "jsx": "preserve",
        "noImplicitAny": false,
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env",
      "jest"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

其他版本的 tsconfig.json, 重点在 pathstypes

{
  "compilerOptions": {
    "target": "esnext", // 编译目标语法, 可以写es5, 但是我们项目的ts-loader前经过了babel-loader
    "module": "esnext", // 
    "strict": true,
        "strictPropertyInitialization": false, // strict为true时,默认为true
        "strictNullChecks": false, // 设置null为其他类型的子类型, 效果:变量或者属性可以初始化为null
    "jsx": "preserve",
        "noImplicitAny": false, // false表示运行隐式的any类型,也就是允许不设置任何类型, 这个设置运行js文件直接改成ts文件
    "importHelpers": true,
    "moduleResolution": "node", // 和nodejs一样的node_modules机制
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "paths": { // 配合baseUrl, ts文件中import 模块路径的解析规则
      "@/*": [
        "src/*",
        "src/types/*"
      ],
      "*":[
        "node_modules/*",
        "src/types/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

以上两个版本主要是 pathstypes 不同, 你需要了解他们的作用,并在工作中设置合适的值.
第二种的其他版本运行把所有的 .d.ts 文件放到 src/types 文件夹内.

src 文件下新建一个 ts 文件

src 下新建 shims-vue.d.ts 文件, 否则会报错如下:

ERROR
      TS18003: No inputs were found in config file 'tsconfig.json'. Specified 'include' paths were '["src/**/*.ts","src/**/*.tsx","src/**/*.vue","tests/**/*.ts","tests/**/*.tsx"]' and 'exclude' paths were '["node_modules"]'.

vueCli3typescript 版本有 shims-vue.d.tsshims-tsx.d.ts 这两个文件,我们不妨把他们放到 src 下.

// shim-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}
// shims-tsx.d.ts
import Vue, { VNode } from 'vue'

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any
    }
  }
}

以上文件的原理,详见 typescriptmodule-augmentation(模块补充: 可以通过路径在文件中增补类型定义)

增加 eslint 规则

解决使用 ts 内置类型时保报错 ,例如 Partial.
安装(第一步已经安装)

yarn add @typescript-eslint/parser --dev

配置 .eslintrc.js 文件的 parser 项为 @typescript-eslint/parser

  • vue-cli(js版) 生成的 .eslintrc.js 中简单修改
module.exports = {
  root: true,
  env: {
    node: true,
        browser: true,
        es6: true
  },
  'extends': [
    'plugin:vue/essential',
    'eslint:recommended',
    '@vue/prettier'
  ],
  parserOptions: {
    // parser: 'babel-eslint',
        parser: '@typescript-eslint/parser', // 解析ts文件, 例如识别ts文件的内置类型
        ecmaFeatures: {
          legacyDecorators: true
        }
  },
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  },
  overrides: [
    {
      files: [
        '**/__tests__/*.{j,t}s?(x)',
        '**/tests/unit/**/*.spec.{j,t}s?(x)'
      ],
      env: {
        jest: true
      }
    }
  ]
}

在上述 .eslintrc.js 的配置中, 默认是使用双引号和分号结尾的, 当我在 rules中修改时,会和 prettier 的配置冲突, 因此在根目录下新建 .prettierrc.js 文件,书写

module.exports = { 
    "printWidth": 80, // 每行代码长度(默认80)
    "tabWidth": 2, // 每个tab相当于多少个空格(默认2)
    "useTabs": false, // 是否使用tab进行缩进(默认false)
    "singleQuote": true, // 使用单引号(默认false)
    "semi": false, // 声明结尾使用分号(默认true)
    "trailingComma": "all", // 多行使用拖尾逗号(默认none)
    "bracketSpacing": true, // 对象字面量的大括号间使用空格(默认true)
    "jsxBracketSameLine": false, // 多行JSX中的>放置在最后一行的结尾,而不是另起一行(默认false)
    "arrowParens": "avoid" // 只有一个参数的箭头函数的参数是否带圆括号(默认avoid)
};
  • 其他版本
module.exports = {
  root: true,
  parserOptions: {
      // +++++++++++
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    ecmaFeatures: {
      legacyDecorators: true
    }
  },
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  // plugin:包名/配置名称
  extends: ['plugin:vue/recommended', 'eslint:recommended'],

  // add your custom rules here
  //it is base on https://github.com/vuejs/eslint-config-vue
  rules: {
    "vue/max-attributes-per-line": [2, {
      "singleline": 10,
      "multiline": {
        "max": 1,
        "allowFirstLine": false
      }
    }],
    "vue/singleline-html-element-content-newline": "off",
    "vue/multiline-html-element-content-newline":"off",
    "vue/name-property-casing": ["error", "PascalCase"],
    "vue/no-v-html": "off",
    'accessor-pairs': 2,
    'arrow-spacing': [2, {
      'before': true,
      'after': true
    }],
    'block-spacing': [2, 'always'],
    'brace-style': [2, '1tbs', {
      'allowSingleLine': true
    }],
    'camelcase': [0, {
      'properties': 'always'
    }],
    'comma-dangle': [2, 'never'],
    'comma-spacing': [2, {
      'before': false,
      'after': true
    }],
    'comma-style': [2, 'last'],
    'constructor-super': 2,
    'curly': [2, 'multi-line'],
    'dot-location': [2, 'property'],
    'eol-last': 2,
    'eqeqeq': ["error", "always", {"null": "ignore"}],
    'generator-star-spacing': [2, {
      'before': true,
      'after': true
    }],
    'handle-callback-err': [2, '^(err|error)$'],
    'indent': [2, 2, {
      'SwitchCase': 1
    }],
    'jsx-quotes': [2, 'prefer-single'],
    'key-spacing': [2, {
      'beforeColon': false,
      'afterColon': true
    }],
    'keyword-spacing': [2, {
      'before': true,
      'after': true
    }],
    'new-cap': [2, {
      'newIsCap': true,
      'capIsNew': false
    }],
    'new-parens': 2,
    'no-array-constructor': 2,
    'no-caller': 2,
    'no-console': 'off',
    'no-class-assign': 2,
    'no-cond-assign': 2,
    'no-const-assign': 2,
    'no-control-regex': 0,
    'no-delete-var': 2,
    'no-dupe-args': 2,
    'no-dupe-class-members': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-empty-character-class': 2,
    'no-empty-pattern': 2,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 2,
    'no-extra-parens': [2, 'functions'],
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implied-eval': 2,
    'no-inner-declarations': [2, 'functions'],
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-iterator': 2,
    'no-label-var': 2,
    'no-labels': [2, {
      'allowLoop': false,
      'allowSwitch': false
    }],
    'no-lone-blocks': 2,
    'no-mixed-spaces-and-tabs': 2,
    'no-multi-spaces': 2,
    'no-multi-str': 2,
    'no-multiple-empty-lines': [2, {
      'max': 1
    }],
    'no-native-reassign': 2,
    'no-negated-in-lhs': 2,
    'no-new-object': 2,
    'no-new-require': 2,
    'no-new-symbol': 2,
    'no-new-wrappers': 2,
    'no-obj-calls': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-path-concat': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-regex-spaces': 2,
    'no-return-assign': [2, 'except-parens'],
    'no-self-assign': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow-restricted-names': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-this-before-super': 2,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-undef': 2,
    'no-undef-init': 2,
    'no-unexpected-multiline': 2,
    'no-unmodified-loop-condition': 2,
    'no-unneeded-ternary': [2, {
      'defaultAssignment': false
    }],
    'no-unreachable': 2,
    'no-unsafe-finally': 2,
    'no-unused-vars': [2, {
      'vars': 'all',
      'args': 'none'
    }],
    'no-useless-call': 2,
    'no-useless-computed-key': 2,
    'no-useless-constructor': 2,
    'no-useless-escape': 0,
    'no-whitespace-before-property': 2,
    'no-with': 2,
    'one-var': [2, {
      'initialized': 'never'
    }],
    'operator-linebreak': [2, 'after', {
      'overrides': {
        '?': 'before',
        ':': 'before'
      }
    }],
    'padded-blocks': [2, 'never'],
    'quotes': [2, 'single', {
      'avoidEscape': true,
      'allowTemplateLiterals': true
    }],
    'semi': [2, 'never'],
    'semi-spacing': [2, {
      'before': false,
      'after': true
    }],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, 'never'],
    'space-in-parens': [2, 'never'],
    'space-infix-ops': 2,
    'space-unary-ops': [2, {
      'words': true,
      'nonwords': false
    }],
    'spaced-comment': [2, 'always', {
      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
    }],
    'template-curly-spacing': [2, 'never'],
    'use-isnan': 2,
    'valid-typeof': 2,
    'wrap-iife': [2, 'any'],
    'yield-star-spacing': [2, 'both'],
    'yoda': [2, 'never'],
    'prefer-const': 2,
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
    'object-curly-spacing': [2, 'always', {
      objectsInObjects: false
    }],
    'array-bracket-spacing': [2, 'never']
  }
}

一个注意

如果你写了如下代码, 会报一个错误:

<template>
<div>{{message}}</div>
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator"
@Component
export default class TestTs extends Vue {
  message: string = "hello ts!";
  mounted() {
    setTimeout(()=>{
      this.message = "hello typeScript!"
    },2000)
  }
}
</script>

错误如下:

error  Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.

解决方法: 修改eslintrc.js

parserOptions: {
    ecmaFeatures: {
      legacyDecorators: true
    }
  },

vue-property-decorator 的使用

vue-property-decorator

查看原文

赞 6 收藏 3 评论 0

Pseudo 赞了文章 · 10月4日

10月了,聊聊我今年参加秋招的真实感受

现在已经10月了,都说金九银十,这个说法到现在应该也不对了,在互联网行业,可能都是金七金八,铁九铜十,在现在的秋招的模式下,一般很多公司已经在7月就大规模的展开提前批的招聘了,而且,很多的offer基本上都是在7月和8月发出的,所以,一般来说,到了9月10月的时候,剩下的offer其实已经不多了,这里给大家如果还没有参加秋招的提个醒,互联网找工作,一定得提早准备,不然,到了9月10月就真的后悔莫及了。

前面我也写过,在春招的时候,我选择了去鹅厂实习,在实习的过程中是没有想象中的那么轻松的,你想划水?其实是不太可能的,没的工作压力还是很大,在鹅厂的这几个月的时间里,每天基本上都是高强度的工作,这是很难避免的,每天的需求跟工作是一直在推着你前进的。另外,实习的时候,一般都会想着转正这件事情的,特别是今年,在实习的时候就想过一个问题,按照今年的行情来说,秋招的压力相比于前几年应该是大很多的,今年的疫情影响没有办法避免这种情况,所以,在实习的时候,我为了转正,一般都是工作到比较晚,晚上11点下班是家常便饭,不过,虽然辛苦,但辛苦总算是没有白费,最终还是成功的转正了。

在这里给大家提个醒,如果你没有实习,或者说实习的时候的工作不是你满意的,建议可以提前参加提前批,尽量不要到实习完了再去准备秋招,今年的情况就是这样的,当我实习完了之后,那时候其实还是不知道自己有没有转正的,而身边的很多同学,很多朋友,一般都是参加了提前批的,他们的手里在9月的时候已经手握几个offer了,而你实习回来,还是 0 offer,这样就能看出了差别了,有时候一比较,你可能会发现,不实习,认真准备秋招,其他并没有什么不好的,可能还可以多抓住一些机会。

讲了这么多,还是希望如果要参加校招的提个醒,一定好好把握提前批。

接下来,再说一下我的秋招情况吧,我的情况就是春招找到了实习,最后也是选择了去鹅厂实习,这里说一下,一般实习转正的时间都是比较晚的,可能都是在8月底,所以,如果你准备转正,那么就得放弃提前批,所以,就看你的选择了,如果说我的建议是什么,那我的建议是:如果你不满意实习的工作,那么久趁早去准备提前批,不要犹豫,犹豫就会败北。

当时,我的选择就是老老实实的留下来实习,因为当时的工作其实还是比较满意的,没有说很不好,特别是熟悉了之后,发现其实还是挺舒服的,所以就没有急着说离职,直接准备秋招,后面当然也是顺利转正了,感觉还是做了一个正确的选择,至于为什么正确,后面会谈到。

另外说一下,转正的结果一般没有那么早出结果的,一般都是要9月中旬才知道结果的,收到意向书可能还会更晚。

实习离职之后,我就开始了我的秋招之旅了。

8月底开始,我就开始海投,当然,那个时候,应为还在提前批的时期,所以,我就在牛客网上投了很多“自称”免笔试的公司,投了之后,发现并没有收到面试的通知,最后发现没有一个是免笔试的,要不没有音讯,要不还是有笔试,所以,这个时候我就发现了提前批的重要性了,提前批有很多的公司基本都是免笔试的,只要你的简历差不多ok,那么基本上都有面试的机会;到了正式批就是我这种情况了,投了要不没有消息,要不就是一个冷冷的笔试链接。

因此,我的9月基本上都是在投简历,做笔试中度过的,有时候一天可能有4-6个笔试,而且很多都是冲突的,根本没有办法顾及所有,只能适当的放弃。

最后,说一下9月一个月的战果吧,其实还是挺凄凉的,到目前为止,我总共投递了57家公司,收到了面试机会了只有4家,拿到了3个offer。

可能是我菜吧,做过的笔试有小米,滴滴,美团,阿里,字节,爱奇艺,其中很多个都是ac,但是,也没有收到面试通知,也不知道怎么回事,唯一的原因可能就是今年的行情不太好,神仙打架,我等菜菜就没有机会了。

不过幸运的是,通过这一个月,虽然只有4个面试机会,也拿到了斗鱼,58,京东的机会了。

9月过去了,还有10月的机会,10月其实机会已经不多了,有的也是补招,部门比较边缘的那种,后面只能看运气了。

经过这次秋招,最大的经验,也是最想跟大家说的就是:一定要早点准备,能参加提前批就参加提前批。

查看原文

赞 2 收藏 0 评论 2

Pseudo 收藏了文章 · 9月30日

使用ESLint+Prettier来统一前端代码风格

加分号还是不加分号?tab还是空格?你还在为代码风格与同事争论得面红耳赤吗?

正文之前,先看个段子放松一下: 去死吧!你这个异教徒!

想起自己刚入行的时候,从svn上把代码checkout下来,看到同事写的代码,大括号居然换行了。心中暗骂,这个人是不是个**,大括号为什么要换行?年轻气盛的我,居然满腔怒火,将空行一一删掉。
但是关于代码风格,我们很难区分谁对谁错,不同的人有不同偏好,唯有强制要求才能规避争论。

所以,团队关于代码风格必须遵循两个基本原则:

  1. 少数服从多数;
  2. 用工具统一风格。

本文将介绍,如何使用ESLint + Prettier来统一我们的前端代码风格。

Prettier是什么?

首先,对应ESLint大多都很熟悉,用来进行代码的校验,但是Prettier(直译过来就是"更漂亮的"😂)听得可能就比较少了。js作为一门灵活的弱类型语言,代码风格千奇百怪,一千个人写js就有一千种写法。虽然js没有官方推荐的代码规范,不过社区有些比较热门的代码规范,比如standardjsairbnb。使用ESLint配合这些规范,能够检测出代码中的潜在问题,提高代码质量,但是并不能完全统一代码风格,因为这些代码规范的重点并不在代码风格上(虽然有一些限制)。

下面开始安利,Prettier。

Prettier是一个能够完全统一你和同事代码风格的利器,假如你有个c++程序员转行过来写前端的同事,你发现你们代码风格完全不一样,你难道要一行行去修改他的代码吗,就算你真的去改,你的需求怎么办,所以没有人真的愿意在保持代码风格统一上面浪费时间。选择Prettier能够让你节省出时间来写更多的bug(不对,是修更多的bug),并且统一的代码风格能保证代码的可读性。

看看Prettier干的好事。

gif
gif

能支持jsx

gif

也能支持css

gif

唯一的遗憾是,暂时还不能格式化vue模版文件中template部分。

ESLint 与 Prettier配合使用

首先肯定是需要安装prettier,并且你的项目中已经使用了ESLint,有eslintrc.js配置文件。

npm i -D prettier

配合ESLint检测代码风格

安装插件:

npm i -D eslint-plugin-prettier

eslint-plugin-prettier插件会调用prettier对你的代码风格进行检查,其原理是先使用prettier对你的代码进行格式化,然后与格式化之前的代码进行对比,如果过出现了不一致,这个地方就会被prettier进行标记。

接下来,我们需要在rules中添加,"prettier/prettier": "error",表示被prettier标记的地方抛出错误信息。

//.eslintrc.js
{
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error"
  }
}

借助ESLint的autofix功能,在保存代码的时候,自动将抛出error的地方进行fix。因为我们项目是在webpack中引入eslint-loader来启动eslint的,所以我们只要稍微修改webpack的配置,就能在启动webpack-dev-server的时候,每次保存代码同时自动对代码进行格式化。

const path = require('path')
module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [path.join(__dirname, 'src')],
        options: {
          fix: true
        }
      }
    ]
}

如果你的eslint是直接通过cli方式启动的,那么只需要在后面加上fix即可,如:eslint --fix

如果与已存在的插件冲突怎么办

npm i -D eslint-config-prettier

通过使用eslint-config-prettier配置,能够关闭一些不必要的或者是与prettier冲突的lint选项。这样我们就不会看到一些error同时出现两次。使用的时候需要确保,这个配置在extends的最后一项。

//.eslintrc.js
{
  extends: [
    'standard', //使用standard做代码规范
    "prettier",
  ],
}

这里有个文档,列出了会与prettier冲突的配置项。

同时使用上面两项配置

如果你同时使用了上述的两种配置,那么你可以通过如下方式,简化你的配置。

//.eslintrc.js
{
  "extends": ["plugin:prettier/recommended"]
}

最后贴一下我们项目中的完整配置,是在vue-cli生成的代码基础上修改的,并且使用standard做代码规范:

module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint'
  },
  env: {
    browser: true,
    es6: true
  },
  extends: [
    // https://github.com/standard/standard/blob/master/docs/RULES-en.md
    'standard',
    // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
    // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
    'plugin:vue/essential',
    "plugin:prettier/recommended",
  ],
  // required to lint *.vue files
  plugins: [
    'vue'
  ],
  // add your custom rules here
  rules: {
    "prettier/prettier": "error",
    // allow async-await
    'generator-star-spacing': 'off',
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  }
}

什么?你们项目没有启用ESLint

不要慌,没有ESLint也不要怕,可以通过onchange进行代码的监听,然后自动格式化代码。只要在package.json的scripts下添加如下代码,然后使用npm run format,我们就能监听src目录下所有的js文件并进行格式化:

"scripts": {
  "format": "onchange 'src/**/*.js' -- prettier --write {{changed}}"
}

当你想格式化的文件不止js文件时,也可以添加多个文件列表。

"scripts": {
  "format": "onchange 'test/**/*.js' 'src/**/*.js' 'src/**/*.vue' -- prettier --write {{changed}}"
}

当然,你也能够在编辑器中配置对prettier的支持,具体支持哪些编辑器,请戳这里

如何对Prettier进行配置

一共有三种方式支持对Prettier进行配置:

  1. 根目录创建.prettierrc 文件,能够写入YML、JSON的配置格式,并且支持.yaml/.yml/.json/.js后缀;
  2. 根目录创建.prettier.config.js 文件,并对外export一个对象;
  3. package.json中新建prettier属性。

下面我们使用prettierrc.js的方式对prettier进行配置,同时讲解下各个配置的作用。

module.exports = {
  "printWidth": 80, //一行的字符数,如果超过会进行换行,默认为80
  "tabWidth": 2, //一个tab代表几个空格数,默认为80
  "useTabs": false, //是否使用tab进行缩进,默认为false,表示用空格进行缩减
  "singleQuote": false, //字符串是否使用单引号,默认为false,使用双引号
  "semi": true, //行位是否使用分号,默认为true
  "trailingComma": "none", //是否使用尾逗号,有三个可选值"<none|es5|all>"
  "bracketSpacing": true, //对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
  "parser": "babylon" //代码的解析引擎,默认为babylon,与babel相同。
}

配置大概列出了这些,还有一些其他配置可以在官方文档进行查阅。

注意一点,parser的配置项官网列出了如下可选项:

  • babylon
  • flow
  • typescript Since v1.4.0
  • postcss Since v1.4.0
  • json Since v1.5.0
  • graphql Since v1.5.0
  • markdown Since v1.8.0

但是如果你使用了vue的单文件组件形式,记得将parser配置为vue,目前官方文档没有列出来。当然如果你自己写过AST的解析器,也可以用你自己的写的parser: require("./my-parser")

总结

有了prettier我们再也不用羡慕隔壁写golang的同事,保存后就能自动format,也不用为了项目代码不统一和同事争论得面红耳赤,因为我们统一使用prettier的风格。可能刚开始有些地方你看不惯,不过不要紧,想想这么做都是为了团队和睦,世界和平,我们做出的牺牲都是必要的。而且prettier的样式风格已经在很多大型开源项目中被采用,比如react、webpack、babel。

他们都在用

你看,他们都在用了,你还在等什么,想变成异教徒被烧死吗,还不快行动起来。更多精彩内容请看官方链接

image

查看原文

Pseudo 赞了文章 · 9月30日

使用ESLint+Prettier来统一前端代码风格

加分号还是不加分号?tab还是空格?你还在为代码风格与同事争论得面红耳赤吗?

正文之前,先看个段子放松一下: 去死吧!你这个异教徒!

想起自己刚入行的时候,从svn上把代码checkout下来,看到同事写的代码,大括号居然换行了。心中暗骂,这个人是不是个**,大括号为什么要换行?年轻气盛的我,居然满腔怒火,将空行一一删掉。
但是关于代码风格,我们很难区分谁对谁错,不同的人有不同偏好,唯有强制要求才能规避争论。

所以,团队关于代码风格必须遵循两个基本原则:

  1. 少数服从多数;
  2. 用工具统一风格。

本文将介绍,如何使用ESLint + Prettier来统一我们的前端代码风格。

Prettier是什么?

首先,对应ESLint大多都很熟悉,用来进行代码的校验,但是Prettier(直译过来就是"更漂亮的"😂)听得可能就比较少了。js作为一门灵活的弱类型语言,代码风格千奇百怪,一千个人写js就有一千种写法。虽然js没有官方推荐的代码规范,不过社区有些比较热门的代码规范,比如standardjsairbnb。使用ESLint配合这些规范,能够检测出代码中的潜在问题,提高代码质量,但是并不能完全统一代码风格,因为这些代码规范的重点并不在代码风格上(虽然有一些限制)。

下面开始安利,Prettier。

Prettier是一个能够完全统一你和同事代码风格的利器,假如你有个c++程序员转行过来写前端的同事,你发现你们代码风格完全不一样,你难道要一行行去修改他的代码吗,就算你真的去改,你的需求怎么办,所以没有人真的愿意在保持代码风格统一上面浪费时间。选择Prettier能够让你节省出时间来写更多的bug(不对,是修更多的bug),并且统一的代码风格能保证代码的可读性。

看看Prettier干的好事。

gif
gif

能支持jsx

gif

也能支持css

gif

唯一的遗憾是,暂时还不能格式化vue模版文件中template部分。

ESLint 与 Prettier配合使用

首先肯定是需要安装prettier,并且你的项目中已经使用了ESLint,有eslintrc.js配置文件。

npm i -D prettier

配合ESLint检测代码风格

安装插件:

npm i -D eslint-plugin-prettier

eslint-plugin-prettier插件会调用prettier对你的代码风格进行检查,其原理是先使用prettier对你的代码进行格式化,然后与格式化之前的代码进行对比,如果过出现了不一致,这个地方就会被prettier进行标记。

接下来,我们需要在rules中添加,"prettier/prettier": "error",表示被prettier标记的地方抛出错误信息。

//.eslintrc.js
{
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error"
  }
}

借助ESLint的autofix功能,在保存代码的时候,自动将抛出error的地方进行fix。因为我们项目是在webpack中引入eslint-loader来启动eslint的,所以我们只要稍微修改webpack的配置,就能在启动webpack-dev-server的时候,每次保存代码同时自动对代码进行格式化。

const path = require('path')
module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [path.join(__dirname, 'src')],
        options: {
          fix: true
        }
      }
    ]
}

如果你的eslint是直接通过cli方式启动的,那么只需要在后面加上fix即可,如:eslint --fix

如果与已存在的插件冲突怎么办

npm i -D eslint-config-prettier

通过使用eslint-config-prettier配置,能够关闭一些不必要的或者是与prettier冲突的lint选项。这样我们就不会看到一些error同时出现两次。使用的时候需要确保,这个配置在extends的最后一项。

//.eslintrc.js
{
  extends: [
    'standard', //使用standard做代码规范
    "prettier",
  ],
}

这里有个文档,列出了会与prettier冲突的配置项。

同时使用上面两项配置

如果你同时使用了上述的两种配置,那么你可以通过如下方式,简化你的配置。

//.eslintrc.js
{
  "extends": ["plugin:prettier/recommended"]
}

最后贴一下我们项目中的完整配置,是在vue-cli生成的代码基础上修改的,并且使用standard做代码规范:

module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint'
  },
  env: {
    browser: true,
    es6: true
  },
  extends: [
    // https://github.com/standard/standard/blob/master/docs/RULES-en.md
    'standard',
    // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
    // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
    'plugin:vue/essential',
    "plugin:prettier/recommended",
  ],
  // required to lint *.vue files
  plugins: [
    'vue'
  ],
  // add your custom rules here
  rules: {
    "prettier/prettier": "error",
    // allow async-await
    'generator-star-spacing': 'off',
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  }
}

什么?你们项目没有启用ESLint

不要慌,没有ESLint也不要怕,可以通过onchange进行代码的监听,然后自动格式化代码。只要在package.json的scripts下添加如下代码,然后使用npm run format,我们就能监听src目录下所有的js文件并进行格式化:

"scripts": {
  "format": "onchange 'src/**/*.js' -- prettier --write {{changed}}"
}

当你想格式化的文件不止js文件时,也可以添加多个文件列表。

"scripts": {
  "format": "onchange 'test/**/*.js' 'src/**/*.js' 'src/**/*.vue' -- prettier --write {{changed}}"
}

当然,你也能够在编辑器中配置对prettier的支持,具体支持哪些编辑器,请戳这里

如何对Prettier进行配置

一共有三种方式支持对Prettier进行配置:

  1. 根目录创建.prettierrc 文件,能够写入YML、JSON的配置格式,并且支持.yaml/.yml/.json/.js后缀;
  2. 根目录创建.prettier.config.js 文件,并对外export一个对象;
  3. package.json中新建prettier属性。

下面我们使用prettierrc.js的方式对prettier进行配置,同时讲解下各个配置的作用。

module.exports = {
  "printWidth": 80, //一行的字符数,如果超过会进行换行,默认为80
  "tabWidth": 2, //一个tab代表几个空格数,默认为80
  "useTabs": false, //是否使用tab进行缩进,默认为false,表示用空格进行缩减
  "singleQuote": false, //字符串是否使用单引号,默认为false,使用双引号
  "semi": true, //行位是否使用分号,默认为true
  "trailingComma": "none", //是否使用尾逗号,有三个可选值"<none|es5|all>"
  "bracketSpacing": true, //对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
  "parser": "babylon" //代码的解析引擎,默认为babylon,与babel相同。
}

配置大概列出了这些,还有一些其他配置可以在官方文档进行查阅。

注意一点,parser的配置项官网列出了如下可选项:

  • babylon
  • flow
  • typescript Since v1.4.0
  • postcss Since v1.4.0
  • json Since v1.5.0
  • graphql Since v1.5.0
  • markdown Since v1.8.0

但是如果你使用了vue的单文件组件形式,记得将parser配置为vue,目前官方文档没有列出来。当然如果你自己写过AST的解析器,也可以用你自己的写的parser: require("./my-parser")

总结

有了prettier我们再也不用羡慕隔壁写golang的同事,保存后就能自动format,也不用为了项目代码不统一和同事争论得面红耳赤,因为我们统一使用prettier的风格。可能刚开始有些地方你看不惯,不过不要紧,想想这么做都是为了团队和睦,世界和平,我们做出的牺牲都是必要的。而且prettier的样式风格已经在很多大型开源项目中被采用,比如react、webpack、babel。

他们都在用

你看,他们都在用了,你还在等什么,想变成异教徒被烧死吗,还不快行动起来。更多精彩内容请看官方链接

image

查看原文

赞 83 收藏 68 评论 16

Pseudo 收藏了文章 · 9月25日

vue + typescript 项目起手式

vue + typescript 新项目起手式

最后更新于2018-06-30,技术文具有时效性,请知悉

我知道你们早就想用上 vue + ts 强类型了

还有后续 vue + typescript 进阶篇

  • 安装vue-cli
  • 安装ts依赖
  • 配置 webpack
  • 添加 tsconfig.json
  • 添加 tslint.json
  • ts 识别 .vue
  • 改造 .vue文件

什么是typescript

TypeScriptJavaScript 的强类型版本。然后在编译期去掉类型和特有语法,生成纯粹的 JavaScript 代码。由于最终在浏览器中运行的仍然是 JavaScript,所以 TypeScript 并不依赖于浏览器的支持,也并不会带来兼容性问题。

TypeScriptJavaScript 的超集,这意味着他支持所有的 JavaScript 语法。并在此之上对 JavaScript 添加了一些扩展,如 class / interface / module 等。这样会大大提升代码的可阅读性。

与此同时,TypeScript 也是 JavaScript ES6 的超集,GoogleAngular 2.0 也宣布采用 TypeScript 进行开发。这更是充分说明了这是一门面向未来并且脚踏实地的语言。

强类型语言的优势在于静态类型检查,具体可以参见 http://www.zhihu.com/question... 的回答。概括来说主要包括以下几点:

  1. 静态类型检查
  2. IDE 智能提示
  3. 代码重构
  4. 可读性
静态类型检查可以避免很多不必要的错误, 不用在调试的时候才发现问题

前言

随着vue2.5 更好的 TypeScript 集成,同时因为新开项目的契机,故准备动手尝试一下typescript + vue

都说ts万般好,不如一个段子来的直观,一个程序员自从用上了ts之后,连续写了3000+行代码一次编译通过一气呵成,然后很激动的打电话跟老婆炫耀这件事情,老婆回了一句

之前看文章或者 demo 一直认为 vue + typescript 之后就不能友好的写.vue单文件,并且我也在各种 live 中问vue + ts 或者 flow的集成,也一直没有问出什么好的实践,但是本上强上ts的念头,一个字,就是干!

终于决定自己动手,那接下来从 vue-cli 开始配置 ts,看看事实上集成 ts 的体验到底是如何呢?


先贴一张最后配置完毕的.vue文件 ,template 和 style 跟以前的写法保持一致,只有 script 的变化

图片描述

起手vue-cli

这步应该不用写了

Vue 引入 TypeScript

首先Cli之后,接下来需要安装一些必要/以后需要的插件

安装vue的官方插件
npm i vue-class-component vue-property-decorator --save

// ts-loader typescript 必须安装,其他的相信你以后也会装上的
npm i ts-loader typescript tslint tslint-loader tslint-config-standard --save-dev

这些库大体的作用,可以按需引入:

配置 webpack

首先找到./build/webpack.base.conf.js

  • 找到entry.appmain.js 改成 main.ts, 顺便把项目文件中的main.js也改成main.ts, 里面内容保持不变
entry: {
  app: './src/main.ts'
}
  • 找到resolve.extensions 里面加上.ts 后缀 (是为了之后引入.ts的时候不写后缀)
  resolve: {
    extensions: ['.js', '.vue', '.json', '.ts'],
    alias: {
      '@': resolve('src')
    }
  }
  • 找到module.rules 添加webpack对.ts的解析
module: {
  rules: [
    {
      test: /\.(js|vue)$/,
      loader: 'eslint-loader',
      enforce: 'pre',
      include: [resolve('src'), resolve('test')],
      options: {
        formatter: require('eslint-friendly-formatter')
      }
    },
// 从这里复制下面的代码就可以了
    {
      test: /\.ts$/,
      exclude: /node_modules/,
      enforce: 'pre',
      loader: 'tslint-loader'
    },
    {
      test: /\.tsx?$/,
      loader: 'ts-loader',
      exclude: /node_modules/,
      options: {
        appendTsSuffixTo: [/\.vue$/],
      }
    },
// 复制以上的
  }
}

是不是加完了,那现在来解释一下

ts-loader 会检索当前目录下的 tsconfig.json 文件,根据里面定义的规则来解析.ts文件(就跟.babelrc的作用一样)

tslint-loader 作用等同于 eslint-loader

添加 tsconfig.json

接下来在根路径下创建tsconfig.json文件

这里有一份参考的 tsconfig.json 配置,完成的配置请点击 tsconfig.json

{
  // 编译选项
  "compilerOptions": {
    // 输出目录
    "outDir": "./output",
    // 是否包含可以用于 debug 的 sourceMap
    "sourceMap": true,
    // 以严格模式解析
    "strict": true,
    // 采用的模块系统
    "module": "esnext",
    // 如何处理模块
    "moduleResolution": "node",
    // 编译输出目标 ES 版本
    "target": "es5",
    // 允许从没有设置默认导出的模块中默认导入
    "allowSyntheticDefaultImports": true,
    // 将每个文件作为单独的模块
    "isolatedModules": false,
    // 启用装饰器
    "experimentalDecorators": true,
    // 启用设计类型元数据(用于反射)
    "emitDecoratorMetadata": true,
    // 在表达式和声明上有隐含的any类型时报错
    "noImplicitAny": false,
    // 不是函数的所有返回路径都有返回值时报错。
    "noImplicitReturns": true,
    // 从 tslib 导入外部帮助库: 比如__extends,__rest等
    "importHelpers": true,
    // 编译过程中打印文件名
    "listFiles": true,
    // 移除注释
    "removeComments": true,
    "suppressImplicitAnyIndexErrors": true,
    // 允许编译javascript文件
    "allowJs": true,
    // 解析非相对模块名的基准目录
    "baseUrl": "./",
    // 指定特殊模块的路径
    "paths": {
      "jquery": [
        "node_modules/jquery/dist/jquery"
      ]
    },
    // 编译过程中需要引入的库文件的列表
    "lib": [
      "dom",
      "es2015",
      "es2015.promise"
    ]
  }
}

顺便贴一份自己的配置

{
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ],
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "allowJs": true,
    "module": "esnext",
    "target": "es5",
    "moduleResolution": "node",
    "isolatedModules": true,
    "lib": [
      "dom",
      "es5",
      "es2015.promise"
    ],
    "sourceMap": true,
    "pretty": true
  }
}

添加 tslint.json

在根路径下创建tslint.json文件

这里就很简单了,就是 引入 tsstandard 规范

{
  "extends": "tslint-config-standard",
  "globals": {
    "require": true
  }
}

让 ts 识别 .vue

由于 TypeScript 默认并不支持 *.vue 后缀的文件,所以在 vue 项目中引入的时候需要创建一个 vue-shim.d.ts 文件,放在项目项目对应使用目录下,例如 src/vue-shim.d.ts

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}
敲黑板,下面有重点!

意思是告诉 TypeScript*.vue 后缀的文件可以交给 vue 模块来处理。

而在代码中导入 *.vue 文件的时候,需要写上 .vue 后缀。原因还是因为 TypeScript 默认只识别 *.ts 文件,不识别 *.vue 文件:

import Component from 'components/component.vue'

改造 .vue 文件

在这之前先让我们了解一下所需要的插件(下面的内容需要掌握es7装饰器, 就是下面使用的@符号)

vue-class-component

vue-class-componentVue 组件进行了一层封装,让 Vue 组件语法在结合了 TypeScript 语法之后更加扁平化:

<template>
  <div>
    <input v-model="msg">
    <p>msg: {{ msg }}</p>
    <p>computed msg: {{ computedMsg }}</p>
    <button @click="greet">Greet</button>
  </div>
</template>

<script lang="ts">
  import Vue from 'vue'
  import Component from 'vue-class-component'

  @Component
  export default class App extends Vue {
    // 初始化数据
    msg = 123

    // 声明周期钩子
    mounted () {
      this.greet()
    }

    // 计算属性
    get computedMsg () {
      return 'computed ' + this.msg
    }

    // 方法
    greet () {
      alert('greeting: ' + this.msg)
    }
  }
</script>

上面的代码跟下面的代码作用是一样的

export default {
  data () {
    return {
      msg: 123
    }
  }

  // 声明周期钩子
  mounted () {
    this.greet()
  }

  // 计算属性
  computed: {
    computedMsg () {
      return 'computed ' + this.msg
    }
  }

  // 方法
  methods: {
    greet () {
      alert('greeting: ' + this.msg)
    }
  }
}

vue-property-decorator

vue-property-decorator 是在 vue-class-component 上增强了更多的结合 Vue 特性的装饰器,新增了这 7 个装饰器:

  • @Emit
  • @Inject
  • @Model
  • @Prop
  • @Provide
  • @Watch
  • @Component (从 vue-class-component 继承)

在这里列举几个常用的@Prop/@Watch/@Component, 更多信息,详见官方文档

import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'

@Component
export class MyComponent extends Vue {
  
  @Prop()
  propA: number = 1

  @Prop({ default: 'default value' })
  propB: string

  @Prop([String, Boolean])
  propC: string | boolean

  @Prop({ type: null })
  propD: any

  @Watch('child')
  onChildChanged(val: string, oldVal: string) { }
}

上面的代码相当于:

export default {
  props: {
    checked: Boolean,
    propA: Number,
    propB: {
      type: String,
      default: 'default value'
    },
    propC: [String, Boolean],
    propD: { type: null }
  }
  methods: {
    onChildChanged(val, oldVal) { }
  },
  watch: {
    'child': {
      handler: 'onChildChanged',
      immediate: false,
      deep: false
    }
  }
}

开始修改App.vue文件

  1. script 标签上加上 lang="ts", 意思是让webpack将这段代码识别为typescript 而非javascript
  2. 修改vue组件的构造方式( 跟react组件写法有点类似, 详见官方 ), 如下图
  3. vue-property-decorator语法改造之前代码

clipboard.png

当然也可以直接复制下面的代码替换就可以了

<template>
  <div id="app">
    <img data-original="./assets/logo.png">
    <router-view/>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'

@Component({})
export default class App extends Vue {
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

</style>

接下来用相同的方式修改HelloWorld.vue即可

npm run dev

这个时候运行项目就应该能正常跑起来了

到这里我们的配置就已经结束了

最后

如果按照文章没有配置出来,可以参考此repovue-typescript-starter (安全按照文章一步一步操作的版本)

总的来说,就如本文最初讲,ts 从数据类型、结构入手,通过静态类型检测来增强你代码的健壮性,从而避免 bug 的产生。

同时可以继续使用.vue单文件

而且我个人认为加上了typescript,项目逼格提升2个level,也能让后端大哥们不吐槽js弱语言的诟病了

相信之后 vue 对于 ts 的集成会更加友善,期待尤大之后的动作

还有后续 vue + typescript 进阶篇

参考链接/推荐阅读

查看原文

Pseudo 赞了文章 · 9月25日

vue + typescript 项目起手式

vue + typescript 新项目起手式

最后更新于2018-06-30,技术文具有时效性,请知悉

我知道你们早就想用上 vue + ts 强类型了

还有后续 vue + typescript 进阶篇

  • 安装vue-cli
  • 安装ts依赖
  • 配置 webpack
  • 添加 tsconfig.json
  • 添加 tslint.json
  • ts 识别 .vue
  • 改造 .vue文件

什么是typescript

TypeScriptJavaScript 的强类型版本。然后在编译期去掉类型和特有语法,生成纯粹的 JavaScript 代码。由于最终在浏览器中运行的仍然是 JavaScript,所以 TypeScript 并不依赖于浏览器的支持,也并不会带来兼容性问题。

TypeScriptJavaScript 的超集,这意味着他支持所有的 JavaScript 语法。并在此之上对 JavaScript 添加了一些扩展,如 class / interface / module 等。这样会大大提升代码的可阅读性。

与此同时,TypeScript 也是 JavaScript ES6 的超集,GoogleAngular 2.0 也宣布采用 TypeScript 进行开发。这更是充分说明了这是一门面向未来并且脚踏实地的语言。

强类型语言的优势在于静态类型检查,具体可以参见 http://www.zhihu.com/question... 的回答。概括来说主要包括以下几点:

  1. 静态类型检查
  2. IDE 智能提示
  3. 代码重构
  4. 可读性
静态类型检查可以避免很多不必要的错误, 不用在调试的时候才发现问题

前言

随着vue2.5 更好的 TypeScript 集成,同时因为新开项目的契机,故准备动手尝试一下typescript + vue

都说ts万般好,不如一个段子来的直观,一个程序员自从用上了ts之后,连续写了3000+行代码一次编译通过一气呵成,然后很激动的打电话跟老婆炫耀这件事情,老婆回了一句

之前看文章或者 demo 一直认为 vue + typescript 之后就不能友好的写.vue单文件,并且我也在各种 live 中问vue + ts 或者 flow的集成,也一直没有问出什么好的实践,但是本上强上ts的念头,一个字,就是干!

终于决定自己动手,那接下来从 vue-cli 开始配置 ts,看看事实上集成 ts 的体验到底是如何呢?


先贴一张最后配置完毕的.vue文件 ,template 和 style 跟以前的写法保持一致,只有 script 的变化

图片描述

起手vue-cli

这步应该不用写了

Vue 引入 TypeScript

首先Cli之后,接下来需要安装一些必要/以后需要的插件

安装vue的官方插件
npm i vue-class-component vue-property-decorator --save

// ts-loader typescript 必须安装,其他的相信你以后也会装上的
npm i ts-loader typescript tslint tslint-loader tslint-config-standard --save-dev

这些库大体的作用,可以按需引入:

配置 webpack

首先找到./build/webpack.base.conf.js

  • 找到entry.appmain.js 改成 main.ts, 顺便把项目文件中的main.js也改成main.ts, 里面内容保持不变
entry: {
  app: './src/main.ts'
}
  • 找到resolve.extensions 里面加上.ts 后缀 (是为了之后引入.ts的时候不写后缀)
  resolve: {
    extensions: ['.js', '.vue', '.json', '.ts'],
    alias: {
      '@': resolve('src')
    }
  }
  • 找到module.rules 添加webpack对.ts的解析
module: {
  rules: [
    {
      test: /\.(js|vue)$/,
      loader: 'eslint-loader',
      enforce: 'pre',
      include: [resolve('src'), resolve('test')],
      options: {
        formatter: require('eslint-friendly-formatter')
      }
    },
// 从这里复制下面的代码就可以了
    {
      test: /\.ts$/,
      exclude: /node_modules/,
      enforce: 'pre',
      loader: 'tslint-loader'
    },
    {
      test: /\.tsx?$/,
      loader: 'ts-loader',
      exclude: /node_modules/,
      options: {
        appendTsSuffixTo: [/\.vue$/],
      }
    },
// 复制以上的
  }
}

是不是加完了,那现在来解释一下

ts-loader 会检索当前目录下的 tsconfig.json 文件,根据里面定义的规则来解析.ts文件(就跟.babelrc的作用一样)

tslint-loader 作用等同于 eslint-loader

添加 tsconfig.json

接下来在根路径下创建tsconfig.json文件

这里有一份参考的 tsconfig.json 配置,完成的配置请点击 tsconfig.json

{
  // 编译选项
  "compilerOptions": {
    // 输出目录
    "outDir": "./output",
    // 是否包含可以用于 debug 的 sourceMap
    "sourceMap": true,
    // 以严格模式解析
    "strict": true,
    // 采用的模块系统
    "module": "esnext",
    // 如何处理模块
    "moduleResolution": "node",
    // 编译输出目标 ES 版本
    "target": "es5",
    // 允许从没有设置默认导出的模块中默认导入
    "allowSyntheticDefaultImports": true,
    // 将每个文件作为单独的模块
    "isolatedModules": false,
    // 启用装饰器
    "experimentalDecorators": true,
    // 启用设计类型元数据(用于反射)
    "emitDecoratorMetadata": true,
    // 在表达式和声明上有隐含的any类型时报错
    "noImplicitAny": false,
    // 不是函数的所有返回路径都有返回值时报错。
    "noImplicitReturns": true,
    // 从 tslib 导入外部帮助库: 比如__extends,__rest等
    "importHelpers": true,
    // 编译过程中打印文件名
    "listFiles": true,
    // 移除注释
    "removeComments": true,
    "suppressImplicitAnyIndexErrors": true,
    // 允许编译javascript文件
    "allowJs": true,
    // 解析非相对模块名的基准目录
    "baseUrl": "./",
    // 指定特殊模块的路径
    "paths": {
      "jquery": [
        "node_modules/jquery/dist/jquery"
      ]
    },
    // 编译过程中需要引入的库文件的列表
    "lib": [
      "dom",
      "es2015",
      "es2015.promise"
    ]
  }
}

顺便贴一份自己的配置

{
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ],
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "allowJs": true,
    "module": "esnext",
    "target": "es5",
    "moduleResolution": "node",
    "isolatedModules": true,
    "lib": [
      "dom",
      "es5",
      "es2015.promise"
    ],
    "sourceMap": true,
    "pretty": true
  }
}

添加 tslint.json

在根路径下创建tslint.json文件

这里就很简单了,就是 引入 tsstandard 规范

{
  "extends": "tslint-config-standard",
  "globals": {
    "require": true
  }
}

让 ts 识别 .vue

由于 TypeScript 默认并不支持 *.vue 后缀的文件,所以在 vue 项目中引入的时候需要创建一个 vue-shim.d.ts 文件,放在项目项目对应使用目录下,例如 src/vue-shim.d.ts

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}
敲黑板,下面有重点!

意思是告诉 TypeScript*.vue 后缀的文件可以交给 vue 模块来处理。

而在代码中导入 *.vue 文件的时候,需要写上 .vue 后缀。原因还是因为 TypeScript 默认只识别 *.ts 文件,不识别 *.vue 文件:

import Component from 'components/component.vue'

改造 .vue 文件

在这之前先让我们了解一下所需要的插件(下面的内容需要掌握es7装饰器, 就是下面使用的@符号)

vue-class-component

vue-class-componentVue 组件进行了一层封装,让 Vue 组件语法在结合了 TypeScript 语法之后更加扁平化:

<template>
  <div>
    <input v-model="msg">
    <p>msg: {{ msg }}</p>
    <p>computed msg: {{ computedMsg }}</p>
    <button @click="greet">Greet</button>
  </div>
</template>

<script lang="ts">
  import Vue from 'vue'
  import Component from 'vue-class-component'

  @Component
  export default class App extends Vue {
    // 初始化数据
    msg = 123

    // 声明周期钩子
    mounted () {
      this.greet()
    }

    // 计算属性
    get computedMsg () {
      return 'computed ' + this.msg
    }

    // 方法
    greet () {
      alert('greeting: ' + this.msg)
    }
  }
</script>

上面的代码跟下面的代码作用是一样的

export default {
  data () {
    return {
      msg: 123
    }
  }

  // 声明周期钩子
  mounted () {
    this.greet()
  }

  // 计算属性
  computed: {
    computedMsg () {
      return 'computed ' + this.msg
    }
  }

  // 方法
  methods: {
    greet () {
      alert('greeting: ' + this.msg)
    }
  }
}

vue-property-decorator

vue-property-decorator 是在 vue-class-component 上增强了更多的结合 Vue 特性的装饰器,新增了这 7 个装饰器:

  • @Emit
  • @Inject
  • @Model
  • @Prop
  • @Provide
  • @Watch
  • @Component (从 vue-class-component 继承)

在这里列举几个常用的@Prop/@Watch/@Component, 更多信息,详见官方文档

import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'

@Component
export class MyComponent extends Vue {
  
  @Prop()
  propA: number = 1

  @Prop({ default: 'default value' })
  propB: string

  @Prop([String, Boolean])
  propC: string | boolean

  @Prop({ type: null })
  propD: any

  @Watch('child')
  onChildChanged(val: string, oldVal: string) { }
}

上面的代码相当于:

export default {
  props: {
    checked: Boolean,
    propA: Number,
    propB: {
      type: String,
      default: 'default value'
    },
    propC: [String, Boolean],
    propD: { type: null }
  }
  methods: {
    onChildChanged(val, oldVal) { }
  },
  watch: {
    'child': {
      handler: 'onChildChanged',
      immediate: false,
      deep: false
    }
  }
}

开始修改App.vue文件

  1. script 标签上加上 lang="ts", 意思是让webpack将这段代码识别为typescript 而非javascript
  2. 修改vue组件的构造方式( 跟react组件写法有点类似, 详见官方 ), 如下图
  3. vue-property-decorator语法改造之前代码

clipboard.png

当然也可以直接复制下面的代码替换就可以了

<template>
  <div id="app">
    <img data-original="./assets/logo.png">
    <router-view/>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'

@Component({})
export default class App extends Vue {
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

</style>

接下来用相同的方式修改HelloWorld.vue即可

npm run dev

这个时候运行项目就应该能正常跑起来了

到这里我们的配置就已经结束了

最后

如果按照文章没有配置出来,可以参考此repovue-typescript-starter (安全按照文章一步一步操作的版本)

总的来说,就如本文最初讲,ts 从数据类型、结构入手,通过静态类型检测来增强你代码的健壮性,从而避免 bug 的产生。

同时可以继续使用.vue单文件

而且我个人认为加上了typescript,项目逼格提升2个level,也能让后端大哥们不吐槽js弱语言的诟病了

相信之后 vue 对于 ts 的集成会更加友善,期待尤大之后的动作

还有后续 vue + typescript 进阶篇

参考链接/推荐阅读

查看原文

赞 404 收藏 385 评论 104

认证与成就

  • 获得 191 次点赞
  • 获得 8 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 8 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-12-12
个人主页被 1.6k 人浏览