标签: vue

  • Chat:Vue 进阶必经之路

    Chat:Vue 进阶必经之路

    Vue 进阶必经之路

    Vue 的学习曲线平缓,入门很简单,所以后发夺人,争取到一大批支持者,我也是其中之一。但是因为隐蔽了很多细节,所以我也没少踩坑,挠头拍桌,不厌其烦。如今,我终于突破屏障,遇到大部分问题,都能举一反三,快速定位、快速解决。这段过程,难免追本溯源,穷究细节。我希望通过这次分享,把我的经验和收获传达给大家。

    面向听众:

    1. 有一定 Vue 开发经验,希望进一步提升;
    2. 平时业务代码写得多,想学习技术细节。

    写完了,不得不说,没有预先想象中写得好。原因嘛,当然是工作太忙,能够写文的时间不太够。以后还是平时多写,Chat 当汇总比较好。

    不过还是希望大家都来看,毕竟我这两年踩坑得到的 Vue 经验都在里面了。

    (更多…)
  • CodeMirror 笔记

    CodeMirror 笔记

    这篇文章用来记录使用 CodeMirror 时的一些心得。

    (更多…)
  • Chat idea:Vue 进阶之路

    Chat idea:Vue 进阶之路

    本文记录一下 Vue 里常见的坑,为下一次 GitChat 做准备。

    (更多…)
  • Vue 代码校验工具

    Vue 代码校验工具

    写 Vue 的时候,时不时会发生一些笔误,出的错并不会被编译器和运行时发现,但确实会影响工作,所以想写一个工具把它跑出来。我目前遇到的问题有:

    1. :v-model
    2. attr="false"
  • Vue  小贴士

    Vue 小贴士

    书说简短:

    1. 使用 Vue + Webpack 开发
    2. 使用 CDN 加载依赖
    3. 在开发阶段尽量不要使用压缩的文件,一边取得尽量全面的错误信息

    (更多…)

  • 《Electron + Vue 实战建站工具开发》读者圈开启

    《Electron + Vue 实战建站工具开发》读者圈开启

    今年很努力的做视频培训,收效一般。不过在社区收获了一些声望,还得到一个写书的机会,跟编辑讨论再三,确定下内容:Electron + Vue 实战开发。

    然后呢,也不是我有意拖延,因为很快就入职贵司,一直都很忙,将近两个月,渐渐熟悉了框架,也适应了节奏(希望如此),现在得抓紧时间补书稿了。

    最开始想在知加边写边发,结果知加没挺到现在,所以只好退而求其次,在 SF 开了读者圈——倒不是说 SF 不好,而是他们家的“技术圈”从产品形态上来看,不是非常合适。

    Anyway,欢迎大家加入圈子,您会得到:

    1. 读到这本书全文,并且更早读到
    2. 获得实时问题解答
    3. 得到实时勘误
    4. 得到实体书(具体看出版社给多少了……)

    入圈链接

  • 组件化的度

    组件化的度

    加入贵司后,接手前端开发,主做后台产品。按理说,我在前司做了5年后台产品,轻车熟路,熟的不能再熟,这项工作对我来说应该再合适不过。结果进展非常不顺,甚至老板来敲打我,说工作效率太低,需求做的太慢。

    我也不得不承认,就目前来看,自己确实没有达到很高的效率。不过我也得吐槽一下,这是有原因的!不是我不努力,而是现在的架构实在太太太……(我得选个好词,以免伤害到球哥的感情),太僵硬吧。

    (目前的后台架构是球哥基于 Vue 设计开发的组件化框架。球哥是个码力十足,有追求有实力的人,我非常欣赏他,然则这套框架我实在不敢苟同。)

    Vue 最大的优势,就在于它用很低的成本赋予了 Web MVVM 的能力。于是我们这些前端开发者可以用很低的软件复杂度,完成非常优秀的产品。而且,其作者还尽可能的把 Vue 解耦,降低入门门槛和使用门槛,我们不需要上来就面对一大批陌生的名词和概念,几乎可以直接上手实操。

    可惜,与之同期到来的,还有“组件化”这把双刃剑。组件化当然有好处,它方便我们复用代码,提升效率。但也有很多开发者走上歪路——写组件库,用组件库。写组件库当然有好处,比如,是个扬名立万的好机会。用组件库也有好处,可以提升效率,省去很多开发量。

    但是,所有组件库都有一个避不开的槛——颗粒度。颗粒度太细,用起来跟原生 HTML 差不多,意义不大;颗粒度太粗,一旦不合业务逻辑,就会很蛋疼(对,我就很蛋疼,简直每隔几天蛋就要碎一次……)。怎么样来确定颗粒度的粗细呢?只能看业务了。

    2B 开发的特点

    回到2B业务上。2B开发有几个明显不同于2C开发的地方:

    1. 业务流程又长又复杂
      • 2C 业务,比如微博、微信,用户操作简单直接,效果反馈很快
      • 2B 业务,如果某个操作要填10个表单,经过5个环节审批,那就一个都少不了
    2. 客户多付费,必须尊重他们的需求

    这两点之下,会产生一些要求:

    1. 表单复杂,校验项多,灵活性要求高。此时,表单生成器就不再适用;只要,要把可以生成的简化版,和需要人工的复杂版分开。
    2. 表格多,列多,单元格多变。此时,表格组件也不宜复杂,处理好简单的分页筛选即可,剩下的还是交给业务相关的组件。
    3. 表格表单复用性差,很多表格表单都只出现一次,或者有自己独特的需求。此时,组件的优势不复存在,工作量怎么做都是接近的,反倒是原始的做法更清晰直接。

    2B 开发的组件化

    所以,我认为,2B 的产品,不适宜上来就用组件库搭建复杂的框架。相反,用最简单直接的 HTML、JS 元素,加上 MVVM 特性,效果会更好。

    初期

    最合适的组件化方案就是“不要过早组件化”。组件化的颗粒度以增强原始 HTML 元素为主,比如 Typeahead(输入搜索下拉选择)、给 Input 增加通用的校验工具,等等。用几乎最原始的方式开发,完成需求。这样看起来土,但带来的两个非常重要的好处:

    1. 思路直接,符合直觉,开发者不用投入大量精力学习新框架可以凭直觉完成
    2. 颗粒细,方便实现各种业务逻辑。比如表单想哪几个字段一起校验就一起校验,想怎么显示数据就怎么显示数据。

    中期

    这个时候我们应该完成很多需求了,哪些组件使用频繁值得抽象,哪些组件很复杂但是只用一次,大家心理都有数了,就可以开始着手进行组件化的工作。

    不过,这个时候,仍然不需要做什么表单生成器、表格生成器之类的东西。我们要做的,是强业务相关组件。比如,某个表单在多处被重复使用,我们就要想办法把它抽象成组件(一般用到第3次,就要考虑做组件,用到第4次就一定要做成组件)。做出来的组件,只包含这个表单;抽象的目的:统一管理这个表单,方便别人复用。

    抽象成组件的目的,是让今后的开发更成熟快捷,不是为了提炼出一套通用工具集,放到市场上扬名立万。

    晚期

    这个时候我们应该积累了不少组件,足以应付日常需求,可以快速响应快速开发。此时,组件化以重构为主。

    最终,很遗憾,我们并未收获一套普适的通用组。但是,我们得到一套和业务深度绑定的组件库,和建筑于底层,弹性好鲁棒佳的架构。


    这才是我眼中的组件化之道。

  • 解决 [Vue warn]: You may have an infinite update loop in a component render function

    解决 [Vue warn]: You may have an infinite update loop in a component render function

    今天写着写着,突然发现控制台里有错误:

    [Vue warn]: You may have an infinite update loop in a component render function
    

    这个问题很奇怪,之前从来没有遇到过。如果是我自己主导的项目,倒也好办,慢慢 debug 就是;偏偏在公司的项目里遇到这个问题,而公司项目的体系结构很复杂,我还没完全掌握。更恼火的是,因为体系复杂,debug 也非常困难,再加上尚无测试框架,这个难搞啊……

    好死不死的,当时是下午3、4点钟,正好到了肚饿的时刻,结果又落入低血糖状态,真是屋漏偏逢连阴雨,船小又碰顶头风,饿得我脑仁生疼……

    不过终于还是被我 Google + debug 出来。事实上是这样的,在 v-for 循环当中,如果用方法或者计算属性对 vm.$data 的属性进行操作,理论上,可能因为修改到循环对象,诱发无限循环。此时 Vue 就会发出警告(并不是真的已经无限循环了)。

    例如这样一个组件,它里面是用 :checked + <label> 实现的一组按钮。它有以下功能:

    1. 为了能够分组,需要设置它们的 name 属性
    2. 为了能够用 <label> 控制 <input>,需要给 <input> 设置 id
    3. 按钮可以被删除

    于是我选择这样做:

    <template>
    <div>
      <template v-for="(item, index) in items">
        <input type="checkbox" :name="'my-component-' + selfIndex" :id="getID">
        <label :for="getID(false)">
        <button type="button" @click="remove(index)">&times;</button>
      </template>
    </div>
    </template>
    
    <script>
    let count = 0;
    
    export default {
      data() {
        return {
          selfIndex: 0,
          itemIndex: 0,
        }
      },
      methods: {
        getID(increase = true) { // 注意,问题就出在这里
          if (increase) {
            this.itemIndex++;
          }
          return `my-component-${this.selfIndex}-${this.itemIndex}`;
        },
      },
      beforeMount() {
        this.selfIndex = count;
        count++;
      }
    }
    </script>
    

    这里,为了能生成唯一 ID,我选择每次循环都对 vm.itemIndex++,这就会出现前面说的问题,存在隐患。

    解决的方案有两种,一种是把 itemIndex 也放在局部变量里,使它不直接关联在组件上;另一种则是写一个全局的唯一 ID 生成函数,然后引用进来。原理都是一样的。


    这两天听评书《乱世枭雄》,学到一句话“拉屎脸朝外”,形容讲义气,不知道咋联系的……

  • Vue 里 $mount 方法的作用

    Vue 里 $mount 方法的作用

    今天由同学问道:“vue文档里说如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。这个方法在项目中有什么用呢,是一个组件可以挂载到多个dom上就这一个作用吗?”

    我的答案如下:

    有些时候你创建 vm 的时候 DOM 可能还没准备好,或者写 vm 的时候不确定它要挂在哪个 DOM 上,这个时候就有用了。

    当项目大的时候,很多情况是开发的时候估计不到的。比如 A 类里面想使用一个 B 类,但是 B 类有 10 个子类,对应不同场景,你写 A 类的时候根本不知道产品中会使用哪个 B 的子类,所以就要想办法把控制权交出去,这就叫“控制反转”——即控制权从开发者的手上,交到使用者的手上。

    我觉得 $mount 这里也是类似的,就是你写了一个 Vue 类,但是不能控制使用场景。所以就要有一个 $mount 方法,由使用者控制,绑到目标 DOM 上。

    他又问:“懂了,还有一个问题是一个组件能不能利用$mount属性挂载到多个dom上,我刚刚试了一下是可以渲染,这种用法常见吗??”

    我答:

    我没这么做过,不过我认为是合理的。

    我觉得这个跟 Vue 的定位有关。Vue 和 React、Angular 不一样,后者是大公司企业级产品。Vue 其实很想取代 jQuery,所以这方面其实跟 jq 的插件是很相似的。

  • Vue 笔记:事件

    Vue 笔记:事件

    onload

    需要侦听 load

    <template>
      <img src="/path/to/img" @load="onLoad">
    </template>
    
    <script>
    export default {
      methods: {
        onLoad(event) {
          // do something
        }
      }
    }
    </script>
    

    drop

    需要阻止 dragover 的默认行为,不然不触发。

    <template>
      <div class="drop-area" @dragover.prevent="onDragOver" @drop="onDrop">
    </template>
    
    <script>
    export default {
      methods: {
        onDragOver(event) {
          // 此时仍然可以处理
        },
        onDrop(event) {
          console.log(event.dataTransfer);
        }
      }
    }
    </script>