levy9527

levy9527 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织 github.com/levy9527/blog 编辑
编辑

思考,表达,练习,创造

个人动态

levy9527 分享了头条 · 2020-11-10

如果你觉得现在的工作没有挑战、想去一个更大的团队,让自己的工作能帮助到公司50+以上的前端,那么,你可以来试一试。面试基本条件:使用 google 搜索能阅读全英文档如果不满三年经验,需要满足以下条件之一:最近半年坚持写博客最近半年有参与开源项目协作团队优秀...

赞 0 收藏 0 评论 0

levy9527 发布了文章 · 2019-12-27

不用再看图片优化指南啦:v-img 介绍

介绍

你是否早就看过所谓的万字长文,上面陈述了各种图片优化技巧,看完之后,你点赞、收藏,然后就下文了?

你是否早就听说了webp,但考虑到了兼容性,你又老老实实用回了jpg、png?

你是否在项目中拿到UI切的图片就用,使用 img 元素能显示就行,等项目上线了,才发现图片体积过大、刚打开首屏就发送了几十个图片请求,这时才想到对图片进行压缩、使用懒加载?

现在,使用 vue 技术栈的同学有福利啦。大家不需要再重复上述操作了,使用 v-img 组件,就可以解决上述烦恼。

你不需要掌握图片优化技巧、下载各种图片压缩工具,也不需要考虑 webp 的兼容性,也不用再去找懒加载的类库,这一切组件都帮你做了。

你继续使用 jpg、png,让组件帮你返回 webp,让图片加载更快,更节省流量,就是这样简单!

那么,这个组件到底怎么用呢?很简单,就是把代码里的 img 替换成 v-img 就可以了!

效果展示

说得这么神奇,到底效果如何,我们先来一睹为快。

  • 懒加载

hire-lazy-loading.gif

  • 启用webp/使用阿里云OSS服务

image.png

  • 启用webp/项目私有化部署

image.png

案例分析

下面来看一个项目在引入 v-img 前后的对比。

优化前

在没有使用 v-img 的时候,项目首页的加载情况是这样的:
image.png

如图所示, 首屏图片总体积为 947.1 KB,请求数量为 61 个。

优化后

使用v-img 开启 webp 后:
image.png

如上图所示, 首屏图片体积减少为 668.9 KB。

开启 v-img 的懒加载功能,首页加载情况如下:
image.png

最终,首屏图片总体积为 609.4 KB,图片请求数量减少为 13 个。

也即,使用 v-img 后,首屏总体积大约减少 350 KB,图片请求数量减少约 50 个,而你不需要了解各种图片优化技巧、还有烦琐的兼容性检测,组件都帮你做啦!

揭密

那么, v-img 内部究竟做了什么呢?
其基本设想是:

<v-img data-original="https://domain.com/path/original.jpg" />
  1. 传参原始图片 url
  2. 自动在原 url 添加一系列参数 ?format=webp
  3. 返回优化后的图片

默认配置适用于图片放在阿里云OSS的场景,如果项目中使用的是其他图片服务或私有化部署,可以修改 provider 参数

其核心逻辑如下:

要是想更详细地了解内部机制,可以查阅文档,或者直接阅读源码,文末会附上相关链接。

结语

我们最初做这个组件的想法很简单,就是想在项目中使用webp。

有了这个想法,我们就开始动手设计并实现。

在此期间,把所属的项目全部用 v-img 实践了一波,提高了 Lighthouse 的分数,提升了用户体验。

此次研发 v-img 的经历不同于以往,总结起来有如下几点:

  1. 我们解决的,是自己当前所在场景遇到的问题,而非过去的场景,亦非别人遇到的问题
  2. 我们实践了多个场景(包括使用云服务、私有化部署),而非仅仅满足于单一场景
  3. 此次成果是由多人合作共同产出的,而非以往的一人设计、一两人实现的协作模式。可以说,v-img 是我们集体智慧的结晶?

image.png

总的来说,这是一个值得自豪的成果?。如果你也喜欢,欢迎使用,随手点一颗⭐️。

相关链接

查看原文

赞 0 收藏 0 评论 0

levy9527 发布了文章 · 2019-12-27

Lighthouse Github App

前言

Lighthouse 是谷歌 Web 开发“四件套”之一。
image.png

常用的性能评测工具 PageSpeed 内部就是用 Lighthouse 实现的。
image.png

本文将说明如何安装使用 Lighthouse Github App,借助 Travis CI,把 Lighthouse 引入开源项目的 Pull Reuqest 工作流中,达到性能分析自动化的效果。

本文假设读者对 Travis CI 有一定的了解 ,如果没有,可以查看 Travis CI 教程

说明

分析单页面

Lighthouse 是针对静态页面进行分析的,因此分析前需要先构建前端应用,生成 html。

假设前端应用构建命令为 yarn build , 生成的静态文件目录为 static ,只在 master 分支触发构建。

则 Travis CI 参考配置如下,关键点是:

  • 安装 @lhci/cli
  • 安装 chrome 附件
  • 运行 lhci 

    • 设置静态文件目录
    • 设置要测试的页面(如果是 index.html 则可以省略)
branches:
  only:
    - master
language: node_js
node_js:
- lts/*
before_install:
- yarn global add @lhci/cli
install:
- yarn
- yarn build
script:
- lhci autorun --upload.target=temporary-public-storage --collect.staticDistDir=./static
addons:
  chrome: stable
cache: yarn

复制上面的代码,放到 .travis.yml 里,并提交到仓库中。

点击:https://github.com/apps/lighthouse-ci/installations/new

授权后,保存 token

进入 Travis CI,添加环境变量:LHCI_GITHUB_APP_TOKEN,值为上面保存的 token

向 master 提交 PR,即可
image.png

分析多页面

分析 Travis CI 的日志image.png

可以得知,lighthouse 会启动内置一个随机端口的 web server,通过配置文件里指定的静态文件夹,访问到 index.html。测试了三次后,把诊断结果上传到临时的云存储空间中。

假设现在想测试的不是 index.html,或不止测试一个页面,又该如何操作?

可以通过 collect.url=http://localhost/xxx 来配置,示例如下:

lhci autorun --upload.target=temporary-public-storage --collect.staticDistDir=./ --collect.url=http://localhost/static --collect.url=http://localhost/another-page

注意此时不需要指定端口号

案例

看看实际项目 FEMessage/create-nuxt-app 是怎么做的。

create-nuxt-app 在 CI 时共会生成三个目录,每一个目录构建后都是一个spa,它们通过同一个 index.html 作为统一入口。也即,我们要评估的不是作为统一入口的 index.html,而是三个 spa 的首页。

另外,我们只会在 dev 分支进行 lhci 的性能评估,master 是用于发布npm、生成源码zip包的。

下面是 .travis.yml

branches:
  only:
    - master
    - dev
language: node_js
node_js:
- lts/*
install:
- yarn --frozen-lockfile
script:
  - yarn test
  - yarn generate
  # https://stackoverflow.com/questions/37544306/travis-different-script-for-different-branch
  - test "$TRAVIS_BRANCH" = "dev" && yarn build && ./lhci.sh || echo skip
  - test "$TRAVIS_BRANCH" = "master" &&  yarn zip || echo skip
addons:
  chrome: stable
deploy:
  - provider: script
    skip_cleanup: true
    script: npx semantic-release
    on:
      branch: master
after_script:
  - ./notify.sh

lhci.sh

#!/bin/sh
yarn global add @lhci/cli

lhci autorun --upload.target=temporary-public-storage --collect.staticDistDir=./dist --collect.url=http://localhost/nuxt-element-dashboard --collect.url=http://localhost/nuxt-multiple-spa --collect.url=http://localhost/nuxt-vant

notify.sh

#!/bin/sh
if [ "$TRAVIS_BRANCH" != "master" ]
then
  echo "do not notify cause not on master branch"
  exit 0
fi

if [ "$TRAVIS_TEST_RESULT" != "0" ]
then
  echo "build not success, bye"
  exit 1
fi

GREN_GITHUB_TOKEN=$GITHUB_TOKEN yarn release

结语

Lighthouse 是开源的,也有很多衍生的工具,初次使用,可能学习曲线稍微有些陡峭,建议先从 Lighthouse Github App 入手,先在开源项目中尝试,有了成功经验后,后续再用相应的命令行工具,引入内部项目中。

有同学可能会问,这跟直接使用使用 PageSpeed 进行页面分析有什么不同呢?

这个问题问题得好。可以这样想,使用 PageSpeed 去分析的时候,都是在项目里程碑末期。而使用工具,可以把性能分析引入到研发流程中间,可以自动化,并提前发现问题。更关键的是,这利于增强团队的性能优化意识,为创建性能文化打下基础。

参考

查看原文

赞 0 收藏 0 评论 0

levy9527 发布了文章 · 2019-12-18

el-form-renderer 1.12.1 发布:支持设置动态 options

前言

还在 mounted 里调用接口后再用 setOptions 来更新表单的 options 数组吗??现在不需要这样了!
使用 remote 属性,简单配置一下远端接口 url,即可自动发起请求并更新 options 属性??!
甚至,任何 props 都可以通过 remote 来远程获取?,快来看看吧!

场景

你正在愉快地使用 el-form-renderer 来维护表单。

<template>
  <el-form-renderer :content="content" />
</template>
<script>
  export default {
    data() {
      return {
        content: [
          {
            id: 'name',
            type: 'input',
            label: '姓名',
            rules: [{required: true, message: '请输入姓名'}]
          }
        ]
      }
    }
  }
</script>

所有表单相关的配置都写在一处,体验太棒了。

此时,产品经理又提了个需求,你给表单加了一项 select。

<template>
  <el-form-renderer :content="content" />
</template>
<script>
  export default {
    data() {
      return {
        content: [
          {/* ... */},
          // 新增地址选择器
          {
            id: 'address',
            type: 'select',
            label: '地址',
            options: [
              {label: '广州', value: '0'},
              {label: '北京', value: '1'},
            ]
          }
        ]
      }
    }
  }
</script>

只需修改一处,表单就新增好了。这就是 el-form-renderer 的魔力!

产品经理微微一笑,事情没有那么简单。地址的选项数据是随时需要更新的,所以应当由后台控制;前端从接口获取。那么稍微改一哈?

<template>
  <!-- 新增属性 ref -->
  <el-form-renderer :content="content" ref="formRenderer" />
</template>
<script>
  import addressUrl from '@/service' // 新增
  export default {
    // ……
    mounted() {
      // ……
      this.getAndSetAddressOptions() // 新增调用
    },
    // ……
    methods: {
      // ……
      // 新增函数
      getAndSetAddressOptions() {
        this.$axios.$get(addressUrl)
            .then(options => {
            this.$refs.formRenderer.setOptions({
              address: options
            })
          })
      }
    }
  }
</script>

这,只不过是改了 options 成异步获取。就要修改这么多地方?el-form-renderer 太难用了!

解决方案:remote 属性

使用 remote 属性,可以直接在 data 里面设置动态 options

<template>
  <el-form-renderer :content="content" />
</template>
<script>
  import addressUrl from '@/service' // 新增
  export default {
    data() {
      return {
        content: [
          {
            id: 'name',
            type: 'input',
            label: '姓名',
            rules: [{required: true, message: '请输入姓名'}]
          },
          {
            id: 'address',
            type: 'select',
            label: '地址',
            // 使用 remote api 代替 options
            remote: {url: addressUrl}
          }
        ]
      }
    }
  }
</script>

重构的结果,代码甚至比原来还要少!

另外,这样写还有一个好处:避免了 逻辑关注点过于分散 的问题。而这也是 Vue@3.0 版本的 Composition Api 的设计理念。

image.png

原理

remote 在内部做了什么?

export default {
  watch: {
    remote({url}) {
      this.$axios.get(url).then(resp => setOptions(id, resp.data))
    }
  }
}

核心逻辑就这么简单。默认情况下,el-form-renderer 依赖全局注册的 axios 组件去请求数据,然后用内部的 setOptions 方法更新该表单项配置的 options 属性。简单归简单,带来的好处就是,现在所有的表单配置都可以写回一处,不用再分散到各个方法里了!

高阶用法

除了 url 外,remote 属性还接受更进一步的定制,适用各种场景!

onResponse

假如接口返回的数据需要进一步处理再 setOptions 呢?可以使用 onResponse 钩子来实现这个功能。

{
  remote: {
    url,
    onResponse: resp => resp.map(item => ({label: item.l, value: item.v}))
  }
}

request

不想用 axios 怎么办?可以通过 request 属性来完全掌控请求过程。数据来源可以是任何资源!

{
  remote: {
    request: async () => {
      await this.$store.dispatch('action')
      return this.$store.state.data
    }
  }
}

prop

一般来说,remote 是给 el-select、el-checkbox-group 和 el-radio-group 组件,远程设置其 options 选项来使用的。但有些时候,表单项组件的某些属性同样有可能从远程获取,比如 el-cascader 的options 属性。

查看 element 官方文档,可以看到与 el-select 等上述组件用 el-option 组件来写选项不同,el-cascader 是直接传 options 属性来生成选项的
{
  type: 'cascader',
  el: {
    // options: [] // 原本传给 el-cascader options 属性的位置,现在由 remote 所取代
  },
  remote: {url}
}

是的,你没看错:当 type 是 cascader 时,remote 获取的数据会自动传给 el-cascader 的 options 属性!这是组件层面为 el-cascader 做的优化。

如果你希望自定义选择将 remote 获取的数据放到组件的哪一个属性上,你可以使用 prop 属性(默认值 options),来适配你自己的场景。

{
  type: 'transfer',
  remote: {
    url,
    prop: 'data' // 远程获取 el-transfer 的 data 属性
  }
}

结语

这里还有更多的 remote 示例。如果你觉得 el-form-renderer 好用,欢迎来 GitHub 仓库提 issue、pr 或者点个 star 哦~?

查看原文

赞 0 收藏 0 评论 0

levy9527 发布了文章 · 2019-09-02

?捕获与改写HTTPS请求

前言

本文站在 macOS 用户的角度下,分享一下对 HTTPS 进行请求拦截、对响应进行修改的经验。

要注意的是,本文介绍的工具虽然一定程度上对 Windows 用户也适用 ,但并非所有工具都是免费的。

Proxyman

Proxyman可以免费使用,在安卓/IOS手机上也有相应的解决方案,如果只是监测请求,查看 API 请求头及响应体,这个足够了。

Charles

Charles 是收费的,而且要安装 Java 环境,但它厉害的地方在于,可以改写网络(如修改响应头),因此值得一买。

唯一的缺点就是,官方文档不太好友,界面有一定上手难度,好在文本已有图文并茂的说明。

安装与设置

  • 安装SSL证书

  • 点击安装后,在界面搜索 Charles,找到刚刚安装的证书,点击 总是信任

  • SSL代理设置


改写网络

下面的例子展示了如何改写 HTTPS 请求的响应头。

  • 点击左上角,Structure
  • 找到想改写的请求,右键,点击 Breakpoints

  • Breakpoints Settings

  • 双击编辑详情

  • 取消 Request 的勾选

  • 刷新页面,请求将会被拦截,处理 Pending 状态

  • 此时可以编辑响应

  • 最终,客户端收到的是被改写后的响应

参考

查看原文

赞 0 收藏 0 评论 0

levy9527 发布了文章 · 2019-08-07

我曾经是怎么做面试官的

[](#twf9ts)阅读提示

更新于2019年2月3日:本文过于注重技巧,很多内容笔者已不再使用。这是成长的必经过程,正如独孤求败的剑术精进之路,先注重技巧,再内化,变得不拘泥于形式。

纵然本文内容已不完全与笔者真正的面试风格吻合,考虑到可能有开阔视野,启发思路的作用,对新人仍有参考价值,故分享出来。读者可结合自身实践,批判性地看待本文内容,不需要全盘接受。

[](#w8tsrt)形势分析

在线上沟通的时候,就可以分析双方的形式,即我方究竟是处于优势,还是处于劣势。这将为线下的面试打下了一个基调,不同的形势下,需要采取不同的打法。

[](#d4uigf)优势

我方处于优势,即我方占上风,此时是买方市场。

其中的可能的表现为:

  • 候选人主动发消息沟通,主动发送简历
  • 称呼叫“您”,而不是“你”
  • 主动提出面试
  • 担心不符合岗位要求

[](#p8lfqs)劣势

我方处于劣势,即我方占下风,此时是卖方市场,其本质是吸引力不足。

其中的可能的表现为:

  • 候选人评价质疑我方的能力,点评我方不足
  • 消息“已读”,不回复

很多在劣势情况下, 我们是换一个目标。力挽狂澜的案例还是比较稀少的,性价比也不高。

[](#qh7urv)平局

平局也即所谓“五五开”,互相有需求,互相有筛选,那么,这种要靠线下的发挥,让我方扩大优势,增强吸引力。

[](#6g7sfm)实战

[](#bfe0yk)优势打法

重点是:少犯错,保持优势。

可以采取以下手段:

  • 保持时间紧凑
  • “例行公事”
  • “喜厌不形于色”

[](#fcqzfn)保持时间紧凑

因为在线上已经做了许多准备工作,所以现场并不需要聊太多,个人经验而言,保持在半小时内即可。

我犯过的错误就是,最后问“你还有什么想问的”,对方问一些问题; 我还问“还有呢”,不断地给对方提问的机会,忽略了时间,结果说得越多,“出错”的可能性就越大。

为什么说“出错”?因为如果对方问的是不利我方的问题,此时如实回答,就会降低我方身价; 如果想修饰一下,临场发挥不一定能达到令人满意的水平。只要是回答不好,那就让我方的优势有所丢失。

所以应该控制时间,有效的办法是开场前用手机定个闹钟,则到点后自动提醒,时间到了就得结束谈话,不拖泥带水。

[](#gknmaf)“例行公事”

“例行公事”就是让人感觉流程化,结合时间紧凑,在整个过程中,就不会让对方有多余的思考,一切是我方在带领节奏,我方主导,一切在掌握之中。

如果对不同的人采取不同的流程,虽然灵活,但难免会发意外情况,如果应对不好, 那就减分了,因此还是使用一套自己熟悉的流程,求稳为上。

[](#wznggy)“喜厌不形于色”

脸上面无表情,不过多地表露内心的情绪。

我犯过的错误就是,当对方表现出闪光点时,我脸上表现得略显惊喜,眼里都是满意,结果被对方当场看出来了。

这有什么不妥呢,这会让增长对方的自信,加重自己的筹码。

所以,纵然自己觉得对方不错,达到我方的标准,也不能表现出“你就是我想要的人”形态。不能让对方觉得我方很需要TA,那就成卖方市场了。

这里有个技巧,就是言语,以及态度中,表现出我方的选择很多,处于买方市场的情形。潜意识中向对方表达,我方的候选人很多,多你不多,少你不少。

[](#n6ffen)指出缺点

该方法适用于想要控制主导权的场景,比如候选人有点自大,或不礼貌,或试图控制局面(当然这种情况很少)时,就可以使用。

注:该方法要求使用者需要一定的控场能力,因为指出缺点会导致气氛变僵,所以必须有后续措施能把气氛缓和回来; 另外指出缺点有可能让对方自尊心受损,所以必须注意形式,不激起对方的反抗心理。

[](#m1ygnf)价值观

技术面(或称专业面)很容易出现的一个问题就是,只聊专业能力,而忽略了其他。

这会造成一个问题,可能在最后待遇环节出现问题,候选人只比较薪资,而不考虑其他。于是可能发了offer,候选人觉得别的公司价钱更高,于是没来。

其实可以在面试的时候,输出我方的价值观,给对方“洗脑”,让对方能看到钱以外的东西。

所以进行专业面的时候,有必要在最后了解一下候选人的人物性格,个人想法,以及生活方面的内容,侧面加深对候选人的了解,同时趁此机会输出一波价值观,让他能看到更高维度的东西。

[](#3245vx)平台与项目

之前犯了个错误,为了抬高平台,我采用了打压项目的方式,想以此有个对比,来突出平台的优越感。但这样会让人对项目有所抵触。

更高明的做法是“双赢”,而不是“此消彼长”。也即说明平台与项目是相辅相成的。做项目也没啥不好的,我们的都是百万级甚至千万级别的项目,简历上有这样的经验,那也是很添金的。

[](#tzcrqk)加班

一个人经常加班,很可能是工作方式不当,缺乏技巧,工作效率低,工作能力不行; 一群人经常加班,一定是leader能力不行,一将无能,累死三军。

[](#eyucic)交流

合格的面试应该是有交流的,而不是完全是一问一答的方式。

在面试的最后,可以有个总结。

所谓交流,其关键点有:

  • 换位思考
  • 表达感受
  • 提供价值

如能触类旁通,就会明白,以上三点各类谈话场景中都是适用的。

我以前犯的错误在于,我的态度是强筛选的,但却一路强势到底,没有站在对方的角度,表示理解; 也没有表达感受,肯定对方的能力; 对我方的信息也没有透露过多,没有让对方看到别的价值。

所以这就导致候选人看不到一个更大的平台,更好的成长空间,这才会斤斤计较眼前的既得利益。

当然,这样做也是有历史原因,我曾因为太友好却没有产生好的结果而耿耿于怀。但现在想来,我这是因噎废食,所以我反思调整了过来。

总结一下,以上三点是面试进阶点,需要好好琢磨,反复实践,在实战中摸索出一套适合自己的操作。

查看原文

赞 0 收藏 0 评论 0

levy9527 赞了文章 · 2019-07-27

基于element的表单渲染器 (el-form-renderer)

基于 element-ui 封装的表单渲染器,完整继承了 element 的属性定义,并进行了简单扩展,从而用户能够通过使用一段预设的数据渲染出一个完整的 element 表单。

Usage

// Step1 下载代码
git clone https://github.com/leezng/el-form-renderer.git

// Step2 在需要使用本组件的 .vue 文件中
<template>
  <el-form-renderer :content="content"></el-form-renderer>
</template>

<script>
import ElFormRenderer from 'path/el-form-renderer'

export default {
  components: {
    ElFormRenderer
  }
}
</script>

Props

  • 支持 el-form 上的所有属性。
  • disabled [Boolean] 设置为 true 可禁用所有原子表单。
  • content [ObjectArray] 定义表单的内容,每一个 Object 代表一个原子表单(el-input, el-select, ...),一切 el-form-item 上的属性都在此声明,而对于 el-input 等之上的属性在 $el 属性上进行声明,该 Object 上还存在其他属性,例如: $id, $type, $options[可选]
// content example
[
  {
    $id: "form1", // 每一个原子都存在id,用于存储该原子的值,注意不能重复
    $type: "input", // 类型,element 提供的所有表单类型,即 el-xxx
    label: "输入框", // el-form-item上的属性
    rules: [{ required: true, message: '请输入活动名称', trigger: 'blur' }] // el-form-item上的属性
  }, {
    $id: "form2",
    $type: "select",
    label: "选择框",
    // $el 上用于定义具体原子表单(此处为el-select)的属性
    $el: {
      placeholder: "请选择内容"
    },
    // $options 具有选择功能的原子表单可用此定义可选项,例如: select, radio-group, radio-button, checkbox-group, checkbox-button
    $options: [{
      label: '区域一',
      value: 'shanghai'
    }, {
      label: '区域二',
      value: 'beijing'
    }]
  }
]

Methods

  • 支持 el-form 上的所有方法。
  • getFormValue 获取当前表单的值。

License

MIT

查看原文

赞 6 收藏 7 评论 4

levy9527 发布了文章 · 2019-07-26

💡我们的表单解决方案 el-form-renderer

前言

本文将介绍我们的表单解决方案 @femessage/el-form-renderer,展示我们在 Vue 技术栈下,我们是如何处理以下问题的:

  • 表单项动态显示或隐藏
  • 表单数据联动
  • 表单输入/输出数据格式化
  • 非常规表单项的处理
  • 复杂的表单验证

方案

表单项动态显示或隐藏(hidden)

可以通过 hidden 控制某一表单项的显示或隐藏。

hidden-cn.gif

<template>
  <el-form-renderer ref="form" :content="content" />
</template>

<script>
export default {
  name: 'hidden',
  data() {
    return {
      content: [
        {
          type: 'select',
          id: 'selected',
          label: '选择项目',
          options: [
            {
              label: '项目A',
              value: 'optionA'
            },
            {
              label: '项目B',
              value: 'optionB'
            }
          ]
        },
        {
          label: '资料',
          type: 'input',
          id: 'data',
          el: {
            placeholder: '项目B的具体内容'
          },
          hidden: form => form.selected !== 'optionB' // 如果选择项并非 项目B 则隐藏
        },
      ]
    }
  }
}
</script>

表单数据联动(on)

可以通过 on 来监听 blur , focus 等事件来实现表单联动。

举个例子,填写 fullName 后,自动填充 lastName 

on-cn.gif

<template>
  <el-form-renderer ref="form" :content="content" />
</template>

<script>
export default {
  data() {
    return {
      content: [
        {
          label: '英文名',
          type: 'input',
          id: 'fullName',
          on: {
            blur: ([event], updateForm) => {
              const value = event.target.value
              const lastName = value.split(' ')[1] // 通过空格分割出内容
              updateForm({ lastName })                         // 更新其他表单项
            },
          },
        },
        {
          label: '姓氏',
          type: 'input',
          id: 'lastName',
        }
      ]
    }
  }
}
</script>

输入/输出格式化(inputFormat/outputFormat)

拿 日期范围选择器 为例,组件输出的值是一条字符串,但后端接口格式是两个字段 {startDate, endDate},则此时需要对数据进行格式化处理。

inputFormat

转换输入的数据, 使其变成表单项需要的数据格式

<template>
  <el-form-renderer :content="content" ref="form" />
</template>

<script>
export default {
  data() {
    return {
      content: [
        {
          el: {
            type: 'daterange',
            placeholder: '选择日期',
            valueFormat: 'yyyy-MM-dd'
          },
          type: 'date-picker',
          id: 'date',
          label: '日期',
          // 接口设计的时间范围是两个字段 '2019-07-23','2019-07-24'
          // 处理后的值为 [ '2019-07-23', '2019-07-24' ]
          inputFormat: row => ([row.startDate, row.endDate])
        }
      ]
    }
  }
}
</script>

outputFormat

转换输出的数据, 使其变成需要的(接口期望的)数据格式
input3.gif

<script>
export default {
  data() {
    return {
      content: [
        {
          el: {
            type: 'daterange',
            placeholder: '选择日期',
            valueFormat: 'yyyy-MM-dd'
          },
          type: 'date-picker',
          id: 'date',
          label: '日期', 
          // 处理前的值为 date: [ '2019-07-23', '2019-07-24' ]
          // 处理后的值为 {startDate: '2019-07-23', endDate: '2019-07-24'}
          outputFormat: val => {
            if (!val) {
              return {startDate: '', endDate: ''}
            }
            return {
              startDate: val[0],
              endDate: val[1]
            }
          }
        }
      ]
    }
  }
}
</script>

自定义组件(component)

@femessage/el-form-renderer 默认支持的 type 有限, 只能渲染常见的表单项。对于个性化的需求, 比如想渲染一个上传组件,type 就不够用了, 那怎么办呢? 这时候 component  选项就派上用场了。

component可以渲染自定义组件,而编写自定义组件的关键是在组件内部实现 v-model:

  • 有一个 props 为 value
  • 对外触发 input 事件

input2.gif

<!-- 自定义组件 my-input -->
<template>
  <div class="my-component">
    <el-input :value="value" @input="onInput" />
    <el-button @click="onInput('我帮你输入点东西')">帮我输入点东西</el-button>
  </div>
</template>

<script>
export default {
  props: {
    value: String
  },
  methods: {
    onInput(val) {
      this.$emit('input', val)
    }
  }
}
</script>

则可以用 component  属性让 @femessage/el-form-renderer 渲染此自定义组件

<template>
  <el-form-renderer :content="content"/>
</template>

<script>
import MyInput from '@/components/my-input.vue'
export default {
  data() {
    return {
      content: [
        {
          component: MyInput,
          id: 'myInput',
          label: '自定义输入框组件'
        }
      ]
    }
  },
}
</script>

目前团队对常见的表单扩展组件都根据标准实现了 v-model, 因此都可以不写 template, 由 @femessage/el-form-renderer 实现数据驱动渲染

复杂的表单验证(rules)

一个复杂的表单项配置, 往往需要定义一些规则(rules)来限制用户输入, 规则里面可能还会有自定义的验证器(validator), 这样的表单项多了之后, 就会导致页面文件的配置项变得很长很长。

解决方案是在组件内部设置校验规则, 从而达到封装隐藏目的。 使用者不用关心表单的验证规则,直接引入组件并使用就好。

下面展示一个结合自定义组件(基本输入框)封装的验证规则, 其规则如下:

  • 不允许空值
  • 只能输入3位数或以上
  • 必须以123开头

input2.gif

<template>
  <el-input :value="value" @input="onInput"/>
</template>
<script>
export default {
  rules() {
    return [
      {
        required: true,
        validator: (rule, val, callback) => {
          if (!val) {
            callback(new Error('不能为空!'))
          } else if (!val.length >= 3) {
            callback(new Error('只能输入3位数或以上!'))
          } else if (!/^123/.test(val)) {
            callback(new Error('必须是以123开头!'))
          } else {
            callback()
          }
        }
      }
    ]
  },
  props: ['value'],
  methods: {
    onInput(val) {
      this.$emit('input', val)
    }
  }
}
</script>

同样地,使用 component 渲染此组件

<template>
  <el-form-renderer :content="content"/>
</template>

<script>
import MyInput from '@/components/my-input.vue'
export default {
  data() {
    return {
      content: [
        {
          component: MyInput,
          id: 'myInput',
          label: '规则输入框组件'
        }
      ]
    }
  },
}
</script>

结语

我们内部项目都在使用 @femessage/el-form-renderer,可以在 github 上找到更多信息。

欢迎大家使用,提高项目开发效率~

查看原文

赞 0 收藏 0 评论 0

levy9527 发布了文章 · 2019-07-07

?揭秘vue-sfc-cli: 组件研发利器

前言

本文将揭示vue单文件组件的工具 vue-sfc-cli 的内涵,说明它是如何在整个组件研发流程中提升效率的。

本文可以看成是 ?vue组件发布npm最佳实践 的成长篇,是 ?打造自动化的Github Workflow 的姐妹篇,是团队最佳实践的落地产物,涉及的背景知识有点多,需要花点时间消化?

使用教程

快速开始

npx vue-sfc-cli

# 接下来会有一串的提示,请务必填写
# 推荐kebab-case风格,小写字母,多个单词用-(dash)分隔,如my-component

# 填充完提示后
cd my-component

# 使用git初始化,这样可以使用commit hook
git init

# 安装依赖
yarn

# 开始开发
yarn dev

# 打包
yarn build

# 可以发布了!
yarn publish

参数选项

-u, --upgrade
根据 template目录下模板,生成新的文件,更新到当前组件中。使用的是覆盖策略,默认覆盖的文件定义在 update-files.js。常用于使用最新版本vue-sfc-cli对旧组件的配置进行升级

# cd my-component
npx vue-sfc-cli -u

--files
如果想更新额外的文件,可以传此选项,后接文件名,多个文件使用 , 分隔

npx vue-sfc-cli -u --files package.json,.babelrc.js

--test
生成一个测试的组件模板,常用于ci环境测试。

npx vue-sfc-cli --test

示例文档

在docs目录下,新建 md 文件,建议命名同样是kebab-case

以上传组件upload-to-ali的 docs/draggable.md 文档为例 
image.png

yarn dev 时会转这个markdown文件就会换成demo,可以看到实际代码,还可以实时修改代码,让demo刷新

image.png

API文档

在vue文件里,编写注释,即可生成API文档。

props

在props里使用多行注释
image.png

slot

在slot上一行,使用  @slot 开头的注释
image.png

event

在emit事件上方,使用多行注释
image.png

methods

在要公开显示的方法上方,使用多行注释,并添加 @public
image.png

效果预览
image.png
image.png

引入第三方库

Element-UI为例

yarn add element-ui

新增一个文件:styleguide/element.js

import Vue from 'vue'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(Element)

修改配置文件:styleguide.config.js

image.png

环境变量

如果需要使用环境变量,推荐使用 dotenv 

yarn add dotenv --dev

image.png

prettier and husky

组件模板内置prettier, 可以在提交代码时格式化。

注意的是需要先执行 git init 命令,之后再执行 yarn 安装依赖,否则提交钩子不生效。

注意

不建议在Windows下生成组件,因为.sh可能没有执行权限。 

技术详解

技术概览

  • vue-styleguidist 本地开发demo与生成文档
  • jest 单元测试
  • prettier + husky 代码格式化
  • rollup 打包
  • standard-version 自动生成changelog
  • github-release-notes 生成github release
  • auto-badge 为pr自动添加label
  • netlify 预览pr
  • travis ci 发布npm/github pages
  • shell + dingtalk api 发布成功后发送钉钉消息
  • all-contributors 显示贡献者
  • 内置README.md模板,包括目录结构、返回顶部以及一些常见的badge

模板目录

以下是生成的组件默认模板配置

├── .all-contributorsrc  # all-contributors配置
├── .babelrc 
├── .editorconfig
├── .github
│   └── badge.yml        # github app auto-badge配置,用于给pr自动打标签
├── .gitignore
├── .grenrc.js           # github-release-notes配置
├── .prettierignore      # prettier配置
├── .prettierrc          # prettier配置
├── .travis.yml          # travis ci配置
├── LICENSE              # MIT
├── README.md            # 自述文件
├── build                # rollup配置
│   └── rollup.config.js
├── build.sh             # ci相关文件
├── dist                 # 打包输出目录
├── docs                 # 使用文档,这些md会转换成demo
│   ├── basic.md 
├── notify.sh            # ci相关文件,用于钉钉通知
├── package.json
├── src                   # 源文件目录
│   ├── index.js
│   └── upload-to-ali.vue # 单文件组件
├── styleguide.config.js  # vue-styleguidist配置文件
└── yarn.lock 

开发

选用vue-styleguidist的原因是,好处是:书写md,既可以充当文档,又可以转换成可运行的demo。

这样的好处是,文档与demo一体化,不用同时维护两份代码。

修改md、修改源文件,demo是会hot reload的,非常方便。

测试

对于组件的测试,大家首先想到的是相关的工具集vue-test-utils,然后觉得,组件测试有点难写,或者说,不知道怎么写。

其实可以换个思路,先从简单的做起。做单元测试,更重要的是培养写测试的习惯,所以一开始建议只用jest对纯函数进行测试。

也即,把组件里的方法抽取出来,单独放到一个文件里,然后专门对这些函数进行测试。

这样的好处是,为了方便测试:

  • 开发者在写组件时,需要尽可能地写短小精悍的函数
  • 函数要写成纯函数,也即不依赖或影响到全局变量

这样的方法,也很适合对一个完全没有测试的组件,逐步补充测试用例。

下面是el-data-table对纯函数测试的代码示例

image.png

image.png

附上相关文章:✅使用jest进行测试驱动开发

构建

yarn build 即可构建生成三个文件:

  • upload-to-ali.esm.js
  • upload-to-ali.min.js
  • upload-to-ali.umd.js

使用者import组件时,默认import进来的是 upload-to-ali.umd.js。 关于三个文件的相关描述,?vue组件发布npm最佳实践已阐述过,就不重复了。

rollup的一个特点是,默认不会把组件的依赖一起打包进去,这个特性有利于减少组件的分发体积。

下面是个示例:

// src/index.js
const crypto = require('crypto')
const axios = require('axios')

执行 yarn build , 会得到以下信息
image.png
请不用担心这个警告,这是有意而为之的,因为不想把依赖把打包进dist进去了。

commit规范

在继续下面的内容之前,再复习一下Conventional Commits

摘取重点如下,格式为:

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

其中<type><subject> 是必须的。

type的类型有:

  • feat: A new feature
  • fix: A bug fix
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
  • refactor: A code change that neither fixes a bug nor adds a feature
  • perf: A code change that improves performance
  • test: Adding missing or correcting existing tests
  • chore: Changes to the build process or auxiliary tools and libraries such as documentation generation

另外约定,更新依赖使用 chore(deps),这也是github官方的做法

PR自动打标签

由于github-release-notes生成的release notes只对打上了label的Pull Request才有效,因此给github仓库添加一个自动添加label的机器人,避免重复劳动。

流程详解

.grenrc.js

.grenrc.js是github release notes 的配置, 这是参考了nuxt、github以及其他流行仓库后得到的配置,可以拿来即用
image.png

badge.yml

.github/badge.yml是 auto-badge 的配置文件,需要放到隐藏文件夹 .github 下。以下配置与上面的 .grenrc.js 相对应,同样可以拿来即用

image.png

自动发布

Travis CI

主要利用Travis CI做到自动化,先看下面的 .travis.yml 配置:

image.png

上面参数的具体说明,可以参考教程:?Github集成TravisCI:自动发布

流程详解

其主要流程如下图所示:

build.sh

build.sh内容如下:

#!/bin/sh
yarn stdver

yarn build

git remote add github https://$GITHUB_TOKEN@github.com/FEMessage/upload-to-ali.git > /dev/null 2>&1
git push github HEAD:master --follow-tags

与之相对应的,package.json里scipts需要有以下字段:

"stdver": "standard-version -m '[skip ci] chore(release): v%s'",
"release": "gren release --override"

notify.sh内容如下:

#!/bin/sh
if [ "$TRAVIS_TEST_RESULT" != "0" ]
then
echo "build not success, bye"
exit 1
fi

url=https://api.github.com/repos/FEMessage/upload-to-ali/releases/latest
resp_tmp_file=resp.tmp

curl -H "Authorization: token $GITHUB_TOKEN" $url > $resp_tmp_file

html_url=`cat $resp_tmp_file | sed -n 5p | sed 's/\"html_url\"://g' | awk -F '"' '{print $2}'`
body=`cat $resp_tmp_file | grep body | sed 's/\"body\"://g;s/\"//g'`

msg='{"msgtype": "markdown", "markdown": {"title": "upload-to-ali更新", "text": "@所有人\n# [upload-to-ali]('$html_url')\n'$body'"}}'

curl -X POST https://oapi.dingtalk.com/robot/send\?access_token\=$DINGTALK_ROBOT_TOKEN -H 'Content-Type: application/json' -d "$msg"

rm $resp_tmp_file

这里有两个关键点:

  • 构建失败,不发送消息
  • 调用github api获取最新release时,带上github token

README.md

参考了优秀开源项目后,我们搜索出了一套README.md模板,内置在初始化工程里了
image.png

还有常见的badge
image.png

以及contributors
image.png
相关emoji代表的意思,可以看官方文档

结语

最后,欢迎大家使用https://github.com/FEMessage/vue-sfc-cli开发vue组件~

查看原文

赞 3 收藏 2 评论 0

levy9527 发布了文章 · 2019-06-29

?Github集成TravisCI:自动发布

前言

已经有阮一峰老师的持续集成服务 Travis CI 教程,为什么还要写这篇文章?

原因有二:

  1. 文章内容有些过时
  2. 文章覆盖度不够,有些实践细节没写出来

由于以上原因,纵然可以笔者很快在Github集成Travis CI并成功构建,但在发布时却踩了一些坑,折腾一波才终于发布成功。故写下此文,旨在补充更多的细节,帮助他人少走弯路。

正文

免费购买Travis CI应用

点击 https://github.com/marketplace/travis-ci,登录后免费购买(开源项目集成Travis CI不收费)。

选择关联仓库

选择个人或组织名下需要关联Travis CI的Github仓库。

已经设置过的,想进行修改,可以在Github的 Personal settings-> Applications 中进入。
image.png

编写CI文件

在项目根目录下新建 .travis.yml 文件

touch .travis.yml

发布到github pages

下面展示一个可以发布到gh-pages的例子,可以稍做修改,复制粘贴使用。

该示例包含了:

  • 指定node.js版本
  • 使用yarn进行安装依赖及构建
  • 对安装需要的依赖进行了缓存
  • 设置了两个不含敏感信息的环境变量
  • 设置了一个含有敏感信息的环境变量
  • 把构建生成的文件部署至github pages
language: node_js
node_js:
- lts/*
env:
- API_SERVER=https://easy-mock.com/mock/5c1b3895fe5907404e654045/femessage-mock PUBLIC_PATH=http://levy.work/nuxt-element-dashboard/
# 默认是yarn, 如果有yarn.lock的话
install:
- yarn
# 默认是 yarn test
script:
- yarn build
cache: yarn
deploy:
  provider: pages
  skip-cleanup: true
  keep-history: true
  local-dir: dist
  on:
    branch: master
  github-token: $GITHUB_TOKEN

下面对文件进行说明。

language: node_js
node_js:
- lts/*
  • 第1行指定了构建环境为node.js
  • 第2、3行指定使用node.js最新的LTS版本
env:
- API_SERVER=xxx PUBLIC_PATH=xxx

上面是设置两个环境变量。

注意,一次构建中传多个环境变量,必须写在同一行,使用空格分开。

env:
- API_SERVER=xxx 
- PUBLIC_PATH=xxx

如果写成上面的形式,则会变成两个构建,每一个构建中只有一个环境变量。

install:
- yarn
script:
- yarn build
cache: yarn

上面指定使用yarn进行安装依赖,安装好后执行 yarn build 命令; 为yarn的依赖加速安装,开启了缓存。

下面是最关键的部署配置。

deploy:
  provider: pages
  github-token: $GITHUB_TOKEN
  skip-cleanup: true
  keep-history: true
  local-dir: dist
  on:
    branch: master
  • 第2行指定部署到Github Pages,即仓库的 gh-pages 分支,请确保仓库的pages分支是 gh-pages , 相关操作可以看这里
  • 第3行指定保留构建后的文件
  • 第4行指定每次部署会新增一个提交记录再推送,而不是使用 git push --force
  • 第5行指定构建后要部署的目录
  • 第6、7行指定 master 分支有提交行为时,将触发构建后部署
  • 第8行是部署需要用到的github-token,其中$GITHUB_TOKEN是变量,它可以在Travis CI个人仓库的setting页里设置,相关操作可以看这里

发布到npm

再给出把node.js模块发布到npm的例子

主要是 deploy 这里有所不同

deploy:
  provider: npm
  email: <your_email>
  # api_key: travis encrypt NPM_TOKEN --add deploy.api_key --com
  on:
    branch: master
  skip-cleanup: true

api_key指的的npm的token,可以登录npm后,在个人中心生成

因为不能泄露,所以要通过travis ci的命令行工具进行加密,执行以下命令

travis encrypt NPM_TOKEN --add deploy.api_key --com

复杂例子

下面是一个复杂的例子,也是实际用到的配置,主要是

  • master分支才会触发构建
  • 执行script命令前先读取shell中的环境变量,并生成.env文件
  • 构建成功后

    • 把模块发布到npm
    • 把文档发布到gh-pages
branches:
  only:
    - master
language: node_js
node_js:
- lts/*
git:
  depth: 3
install:
- yarn --frozen-lockfile
before_script: echo OSS_KEY=$OSS_KEY\\nOSS_SECRET=$OSS_SECRET\\nOSS_BUCKET=$=OSS_BUCKET\\nOSS_REGION=$OSS_REGION > .env
script:
- yarn build
cache: yarn
deploy:
- provider: pages
  local-dir: docs
  github-token: $GITHUB_TOKEN
  skip-cleanup: true
  keep-history: true
- provider: npm
  email: levy9527@qq.com
  api_key: $NPM_TOKEN
  skip-cleanup: true

相关操作

使用travis命令行工具加密

加密要用到travis命令行工具,如果是在travis ci web界面设置环境变量,则可直接跳过。

下面给出mac环境下操作需要注意的点

1.安装命令:

brew install travis

否则很可能会出现问题

2.确保在 https://travis-ci.org/ sign in with github

3.然后在项目根目录里,执行命令

travis login —auto

4.修改git设置

vi .git/config

确保

[travis]
  slug = 是你在travis关联的仓库

5.添加加密环境变量

travis encrypt github-token=xxx --add deploy.github-token --com

因为笔者登录的travis ci域名是 https://travis-ci.com,所以要带参数 --com , 默认是 https://travis-ci.org

通过环境变量设置GITHUB_TOKEN

首先为Travis CI新建一个token

点击生成新token

设置权限

image.png
复制生成的token。(记得先不要刷新或离开当前页面,否则token就看不见了,只能重新生成)

登录Travis CI, 进入要集成的项目设置页。

image.png

添加环境变量GITHUB_TOKEN

注意,这里的环境变量是通过bash设置、并在.yml里读取的,所以变量名是大写加下划线形式,这是bash的最佳实践,千万别写成github-token

image.png

GitHub Pages

查看gh-pages分支的部署情况

进入仓库 Settings -> Options

image.png

往下翻看,可以看到效果
image.png
因为笔者自定义了域名,所以地址不是默认的 https://xxx.github.io/xxx

查看原文

赞 1 收藏 1 评论 0

认证与成就

  • 获得 7 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-01-09
个人主页被 708 人浏览