vue 实践技巧合集

前言

本文纯属我的平时实践过程当中的一些经验总结,算是一点点小技巧吧,不是多么高明的技术,若是对你有帮助,那么不胜荣幸。javascript

本文不涉及罕见API使用方法等,大部份内容都是基于对vue的一些实践而已。因为涉嫌投机取巧,可能会带来一些不符合规范的反作用,请根据项目要求酌情使用。html

  1. 多个页面都使用的到方法,放在 vue.prototype 上会很方便 前端

    刚接触 vue 的时候作过一件傻事,由于封装了一个异步请求接口post,放在 post.js 文件里面,而后在每一个须要使用异步请求的页面引入vue

    import port from './xxxx/xxxx/post'

    若是只是这样,还没什么,咱们能够写好一个页面之后再复制,能够保证每一个页面都有上面的语句。可是若是每一个文件所在的目录层级不同呢?java

    // 假设正常是这样
    import port from '../xxxx/xxxx/post'
    // 目录加深一级,就变成这样
    import port from '../../xxxx/xxxx/post'
    // 再加深一级的样子
    import port from '../../../xxxx/xxxx/post'

    固然,这个时候,咱们能够用 别名 @/xxxx/post,可是仍是少不了要每一个页面引用。
    那咱们来看看,用vue.prototype 有多方便?
    首先,你得在 vue 的入口文件( vue-cli 生成的项目的话,默认是 /src/main.js)里面作以下设置ios

    import port from './xxxx/xxxx/post'
    
     vue.prototype.$post = post

    这样,咱们就能够在全部的 vue 组件(页面)里面使用 this.post() 方法了,就像 vue 的亲儿子同样git

    tip: 把方法挂在到 prototype 上的时候,最好加一个 $ 前缀,避免跟其余变量冲突

    til again: 不要挂载太多方法到 prototype 上,只挂载一些使用频率很是高的github

  2. 须要响应的数据,在获取到接口数据的时候,先设置 vuex

    你们有没有很常常碰到这样都一种状况,在循环列表的时候,咱们须要给列表项一个控制显示的属性,如 是否可删除,是否已选中等等,然后端接口通常不会返回这种字段,由于这属于纯前端展现的,跟后端没啥关系,好比后端给的数据以下vue-cli

    [
      {name: 'abc', age: 18},
      {name: 'def', age: 20},
      {name: 'ghi', age: 22},
    ]

    咱们不妨假设以上数据为学生列表

    而后咱们须要渲染这个列表,在每一项后面显示一个勾选按钮,若是用户打勾,则这个按钮是绿色,默认这个按钮是灰色,这个时候,上表是没有知足这个渲染条件的数据,而若是咱们在用户打勾的时候,再去添加这个数据的话,正常的作法是没法及时响应的。

    若是咱们在获取到数据的时候,先给数组的每一项都加一个是否打勾的标示,就能够解决这个问题,咱们假设咱们获取到的数据是 res.list

    res.list.map(item => { 
      item.isTicked = false
    })

    这么作的原理是 vue 没法对不存在的属性做响应,因此咱们在获取到数据的时候,先把须要的属性加上去,而后在赋值给 data , 这样 data 接收到数据的时候,已是存在这个属性了,因此会响应。固然还有其余方法能够实现。不过对于一个强迫症来讲,我仍是比较倾向于这种作法

  3. 封装全局基于 promise 的异步请求方法

    看过不少项目的源码,发现大部分的异步请求都是直接使用 axios 之类的方法,以下

    axios({
      method: 'post',
      url: '/user/12345',
      data: {
        firstName: 'Fred',
        lastName: 'Flintstone'
      }
    })
     .then(function (response) {
        console.log(response);
      })
      .catch(function (error) {
        console.log(error);
      });

    若是有跨域,或者须要设置 http 头等,还须要加入更多的配置,而这些配置,对于同一个项目来讲,基本都是同样的,不同的只有 url 跟参数,既然这样,那我吗为何不把它封装成一个方法呢?

    function post (url,param) {
        return axios({
          method: 'post',
          url: url,
          data: param
          ... axios 的其余配置
        })
    }
    tip: 这里原来我多用了一层promise包起来,对简单的需求来讲是太多余了,感受掘金用户 @日月为易。 指出

    再结合第一点,咱们就能够再任意 vue 实例中这样使用

    let param = {
      firstName: 'Fred',
      lastName: 'Flintstone'
    }
    this.post('/user/12345',param)
    .then(...)
    .catch(...)

    有没有比原始的简单不少呢?若是你的项目支持 async await,还能够这样用

    let param = {
      firstName: 'Fred',
      lastName: 'Flintstone'
    }
    let res  = await this.post('/user/12345',param)
    console.log(res) // res 就是异步返回的数据
    tip: await 关键字必须在 被 async 修饰的函数里面使用
  4. 若是你以为有时候,你真的须要父子组件共享一个值,不如试试传个引用类型过去

    vue 的父子组件传值,有好多种方法,这里就不一一列举了,可是今天咱们要了解的,是利用 javascript 的引用类型特性,还达到另外一种传值的目的

    假设有这么一个需求,父组件须要传 3 个值到子组件,而后再子组件里面改动后,须要立马再父组件上做出响应,咱们一般的作法上改完之后,经过 this.$emit 发射事件,而后再父组件监听对应的事件,然而这么作应对一两个数据还好,若是传的数据多了,会累死人。
    咱们不妨把这些要传递的数据,包再一个对象/数组 里面,而后在传给子组件

    <subComponent :subData="subData"></subComponent>
    data () {
      return {
        subData: {
          filed1: 'field1',
          filed2: 'field2',
          filed3: 'field3',
          filed4: 'field4',
          filed5: 'field5',
        }
      }
    }

    这样,咱们在子组件里面改动 subData 的内容,父组件上就能直接做出响应,无需 this.$emitvuex 并且若是有其余兄弟组件的话,只要兄弟组件也有绑定这个 subData ,那么兄弟组件里面的 subData 也能及时响应

    tip: 首先,这么作我我的上感受有点不符合规范的,若是没有特别多的数据,仍是乖乖用 this.$emit 吧,其次,这个数据须要有特定的条件才能构造的出来,并非全部状况都适用。
  5. 异步请求的参数在 data 里面构造好,用一个对象包起来,会方便不少

    有作过相似 ERP 类型的系统的同窗,必定碰到过这样的一个场景,一个列表,有 N 个过滤条件,这个时候一般咱们这么绑定

    <input type="text" v-model="field1">
     <input type="text" v-model="field2">
     <input type="text" v-model="field3">
     ....
     <input type="text" v-model="fieldn">
    data () {
     return {
       field1: 'value1',
       field2: 'value2',
       field3: 'value3',
       ...
       fieldn:'valuen'
     }
    }

    而后提交数据的时候这样:

    var param = {
       backend_field1: this.field1,
       backend_field2: this.field2,
       backend_field3: this.field3,
       ...
       backend_fieldn: this.fieldn
     }
     this.post(url,param)

    如你看到的,每次提交接口,都要去构造参数,还很容易遗漏,咱们不妨这样:先去接口文档里面看一下后端须要的字段名称,而后

    <input type="text" v-model="queryParam.backend_field1">
        <input type="text" v-model="queryParam.backend_field2">
        <input type="text" v-model="queryParam.backend_field3">
        ....
        <input type="text" v-model="queryParam.backend_fieldn">
    ```javascript
      data () {
       return {
         queryParam:{
           backend_field1: 'value1'
           backend_field2: 'value2'
           backend_field3: 'value3'
           ...
           backend_fieldn: 'valuen'
         }
       }
      }
      ```
      而后提交数据的时候这样:
      ```javascript
       this.post(url,this.queryParam)
      ```

    是的,这样作也是有局限性的,好比你一个数据在 2 个地方共用,好比前端组件绑定的是一个数组,你须要提交给后端的是 2 个字符串(例:element ui 的时间控件),不过部分特殊问题稍微处理一下,也比从新构建一个参数简单不是吗?

  6. data 里面的数据多的时候,给每一个数据加一个备注,会让你后期往回看的时候很清晰

    续上一点,data 里面有不少数据的时候,可能你写的时候是挺清晰的,毕竟都是你本身写的东西,但是过了十天半个月,或者别人看你的代码,相信我,无论是你本身,仍是别人,都是一头雾水(记忆力超出常人的除外),因此咱们不妨给每一个数据后面加一个备注

    data () {
     return {
       field1: 'value1',  // 控制xxx显示
       field2: 'value2',  // 页面加载状态
       field3: [],        // 用户列表
       ...
       fieldn: 'valuen'   // XXXXXXXX
     }
    }
  7. 逻辑复杂的内容,尽可能拆成组件

    假设咱们有一个这样的场景:

    <div>
       <div>姓名:{{user1.name}}</div>
       <div>性别:{{user1.sex}}</div>
       <div>年龄:{{user1.age}}</div>
       ...此处省略999个字段...
       <div>他隔壁邻居的阿姨家小狗的名字:{{user1.petName}}</div>
    </div>
    <-- 固然,显示中咱们不会傻到不用 v-for,咱们假设这种状况没法用v-for -->
    <div>
        <div>姓名:{{user2.name}}</div>
        <div>性别:{{user2.sex}}</div>
        <div>年龄:{{user2.age}}</div>
        ...此处省略999个字段...
        <div>他隔壁邻居的阿姨家小狗的名字:{{user2.petName}}</div>
    </div>

    这种状况,咱们不妨把[用户]的代码,提取到一个组件里面:
    假设以下代码,在 comUserInfo.vue

    <template>
     <div>
       <div>姓名:{{user.name}}</div>
       <div>性别:{{user.sex}}</div>
       <div>年龄:{{user.age}}</div>
       ...此处省略999个字段...
       <div>他隔壁邻居的阿姨家小狗的名字:{{user.petName}}</div>
     </div>
    </template>
    
    <script >
    export  default {
     props:{
       user:{
         type:Object,
         default: () => {}
       }
     }
    }
    </script>

    而后原来的页面能够改为这样(省略掉导入和注册组件,假设注册的名字是 comUserInfo ):

    <comUserInfo :user="user1"/>
    <comUserInfo :user="user2"/>

    这样是否是清晰不少?不用看注释,都能猜的出来,这是2个用户信息模块, 这样作,还有一个好处就是出现错误的时候,你能够更容易的定位到错误的位置。

  8. 若是你只在子组件里面改变父组件的一个值,不妨试试 $emit('input') ,会直接改变 v-model

    咱们正常的父子组件通讯是 父组件经过 props 传给子组件,子组件经过 this.$emit('eventName',value) 通知父组件绑定在 @eventName 上的方法来作相应的处理。
    可是这边有个特例,vue 默认会监听组件的 input 事件,并且会把子组件里面传出来的值,赋给当前绑定到 v-model 上的值

    正经常使用法 - 父组件

    <template>
      <subComponent :data="param" @dataChange="dataChangeHandler"></subComponent>
    </template>
    
    <script >
      export default {
        data () {
          return {
            param:'xxxxxx'
          }
        },
        methods:{
          dataChangeHandler (newParam) {
            this.param = newParam
          }
        }
      }
    </script>

    正经常使用法 - 子组件

    <script >
      export default {
        methods:{
          updateData (newParam) {
            this.$emit('dataChange',newParam)
          }
        }
      }
    </script>

    利用默认 input 事件 - 父组件

    <template>
      <subComponent  v-model="param"></subComponent>
    </template>

    利用默认 input 事件 - 子组件

    <script >
      export default {
        methods:{
          updateData (newParam) {
            this.$emit('input',newParam)
          }
        }
      }
    </script>

    这样,咱们就能省掉父组件上的一列席处理代码,vue 会自动帮你处理好

    tip: 这种方法只适用于改变单个值的状况,且子组件对父组件只需简单的传值,不须要其余附加操做(如更新列表)的状况。

    补充一个 this.$emit('update:fidldName',value) 方法 (感谢掘金用户 @日月为易。 指出)
    具体用法以下:

    父组件

    <subComponent field1.sync="param1" field2.sync="param2"></subComponent>

    子组件

    <script >
      export default {
        methods:{
          updateData1 (newValue) {
            this.$emit('update:field1',newValue)
          },
          updateData2 (newValue) {
            this.$emit('update:field2',newValue)
          }
        }
      }
    </script>

    该方法,我的认为比较适用于 要更新的数据不能绑定在 v-model 的状况下,或者要双向通讯的数据大于 1 个(1个也能够用,但我我的更推荐 input 的方式, 看我的喜爱吧),但又不会不少的状况下.

  9. conponents放在 Vue options 的最上面

    不知道你们有没有这样的经历: 导入组件,而后在也页面中使用,好的,报错了,为啥?忘记注册组件了,为何会常常忘记注册组件呢?由于正常的一个 vue 实例的结构大概是这样的:

    import xxx form 'xxx/xxx'
    export default {
      name: 'component-name',
      data () {
        return {
          // ...根据业务逻辑的复杂程度,这里省略若干行
        }
      },
      computed: {
        // ...根据业务逻辑的复杂程度,这里省略若干行
      },
      created () {
        // ...根据业务逻辑的复杂程度,这里省略若干行
      },
      mounted () {
        // ...根据业务逻辑的复杂程度,这里省略若干行
      },
      methods () {
        // ...根据业务逻辑的复杂程度,这里省略若干行
      },
    }

    我不知道你们正常是把 components 属性放在哪一个位置,反正我以前是放在最底下,结果就是致使常常犯上述错误。

    后面我把 components 调到第一个去了

    import xxx form 'xxx/xxx'
    export default {
      components: {
        xxx
      },
      // 省略其余代码
    }

    今后之后,妈妈不再用担忧我忘记注册组件了,导入和注册都在同一个位置,想忘记都难。

  10. 大部分状况下,生命周期里面,不要有太多行代码,能够封装成方法,再调用

    看过不少代码,包括我本身以前的,在生命周期里面洋洋洒洒的写了一两百行的代码,如:把页面加载的时候,该作的事,所有写在 created 里面,致使整个代码难以阅读,彻底不知道你在页面加载的时候,作了些什么,
    这个时候,咱们不妨把那些逻辑封装成方法,而后在生命周期里面直接调用:

    created () {
      // 获取用户信息
      this.getUserInfo()
      // 获取系统信息
      this.getSystemInfo()
      // 获取配置
      this.getConfigInfo()
    },
    methods:{
      // 获取用户信息
      getUserInfo () {...},
      // 获取系统信息
      getSystemInfo () {...},
      // 获取配置
      getConfigInfo () {...},
    }

    这样是否是一眼就能看的出,你在页面加载的时候作了些什么?

    tip: 这个应该算是一个约定俗成的规范吧,只是以为看的比较多这样写的,加上我本身初学的时候,也这么作了,因此写出来,但愿新入坑的同窗能避免这个问题
  11. 少用 watch,若是你以为你好多地方都须要用到 watch,那十有八九是你对 vueAPI 还不够了解

    vue 自己就是一个数据驱动的框架,数据的变更,能实时反馈到视图上去,若是你想要根据数据来控制试图,正常状况一下配合 computed 服用就能解决大部分问题了,而视图上的变更,咱们通常能够经过监听 input change 等事件,达到实时监听的目的,
    因此不多有需求使用到 watch 的时候,至少我最近到的十来个项目里面,是没有用过 watch 固然,并非说 watch 是确定没用处, vue 提供这个api,确定是有他的道理,也有部分需求是真的须要用到的,只是我以为应该不多用到才对,若是你以为处处都得用到的话,
    那么我以为 十有八九你应该多去熟悉一下 computedvue 的其余 api

最后

本文的github地址 欢迎随意star,follow, 和 不随意的 issue

另外,github上还有其余一些关于前端的教程和组件,有兴趣的童鞋能够看看,大家的支持就是我最大的动力。

相关文章
相关标签/搜索