Vue开发常用的技术点总结

简介

在开发中尤其是团队协作开发中,我们经常会遇到一些重复性问题,由于解决问题后没有做好文档记录,或者没有通过一个有效的渠道进行分享,导致再次遇到同样问题时,还要花费时间经历去查找资料,有时可能还解决不了。

为了避免上诉情况,以后开发中解决问题后,可以通过此文档进行记录,然后分享团队开发人员。

以下是对文档具体内容的一个分类:

  • 基础知识篇:常用的基础技术点,不同的业务场景需要对应不同的配置,对这些技术点掌握不牢固,每次使用时都要翻阅资料去查找示例。
  • 技术分享: 开发中使用的第三方、好用的方法、使用过程遇到的问题。
  • 问题记录:记录开发中遇到的问题,写清楚问题场景、解决方案、解决问题资料链接等。

目的

  • 通过对问题的记录,总结,验证的过程来,有助于我们更深入的理解基础技术知识。
  • 通过新技术的分享,来丰富我们的技术栈。
  • 问题文档完善后,便于团队人员学习查阅,提高团队开发效率。

Vue 基础知识篇

一、指令

1、事件修饰符

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由 开头的指令后缀来表示的.
常用的修饰符:

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
    <!-- 默认事件继续传播 -->
    <div class="parent-box" @click="console('2 - 会执行\n --------结束')">
       <div class="child-box"
       @click="console('开始------\n1 - 会执行')"
       >
            默认
       <div>
   </div>
    <!-- .stop 阻止单击事件继续传播 -->
    <div class="parent-box" @click="console('2 - 不会执行')">
        <div class="child-box" @click.stop="console('1-阻止单击事件继续传播')">
            .stop
        </div>
    </div>
    <!-- .prevent 阻止浏览器事件默认行为 -->
    <div class="parent-box">
        <a
            href="https://www.baidu.com"
            @click.prevent="console('1 - 会输出,不在跳转百度链接')"
        >
            prevent
        </a>
    </div>
    <!-- .native 给组件绑定原生事件 -->
    <div class="parent-box">
        native
        <EventItem
            @clickChild="console('1 - 子组件事件')"
            @click.native="console('2 - 给子组件绑定事件')"
        ></EventItem>
    </div>
    <!-- capture 拦截器,拦截当前事件并有限执行,然后事件继续向下传递 ,
    可以通过.stop阻止传递-->
    <div class="parent-box" @click="console('2 - 会执行')">
        capture
        <EventItem @click.native.capture="console('1 - 子组件事件不会执行')"> </EventItem>
    </div>
    <div class="parent-box" @click="console('2 - 不会执行')">
        capture.stop
        <EventItem @click.native.capture.stop="console('1 - 子组件事件不会执行')"> </EventItem>
    </div>
    <!-- .passive 会告诉浏览器你不想阻止事件的默认行为,这个
        .passive 修饰符尤其能够提升移动端的性能。 -->
    <div class="parent-box" @scroll.passive="console('2 - 不会影响视图的滚动行为')">
        passive
    </div>

二、Class 与 Style 绑定

1、组件上使用 class

  1. 当在一个自定义组件上使用 class property 时,这些 class 将被添加到该组件的根元素上面。

    例如,如果你声明了这个组件:

    Vue.component('my-component', {
        template: '<p class="foo bar">Hi</p>'
    })
    

    然后在使用它的时候添加一些 class:

    <my-component class="baz boo"></my-component>
    

    HTML 将被渲染为:

    <p class="foo bar baz boo">Hi</p>
    
  2. class 优先及情况:

  • 默认 父组件 > 子组件,带 scoped 属性时,scoped 内的样式优先,这个元素上已经存在的 class 会被覆盖。

三、渲染

1、v-if

  1. 因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template>元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。

    <template v-if="ok">
      <h1>Title</h1>
      <p>Paragraph 1</p>
      <p>Paragraph 2</p>
    </template>
    
  2. 避免 v-if 和 v-for 用在一起

    当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,所以这个模板:

    <ul>
         <li
             v-for="user in users"
             v-if="user.isActive"
             :key="user.id"
         >
             {{ user.name }}
         </li>
     </ul>
    
    

    将会经过如下运算:

     this.users.map(function (user) {
         if (user.isActive) {
         return user.name
         }
     })
    
    

    因此哪怕我们只渲染出一小部分用户的元素,也得在每次重渲染的时候遍历整个列表,不论活跃用户是否发生了变化。

    通过将其更换为在如下的一个计算属性上遍历:

    computed: {
        activeUsers: function () {
            return this.users.filter(function (user) {
            return user.isActive
            })
        }
    }
    
    <ul>
        <li
            v-for="user in activeUsers"
            :key="user.id"
        >
            {{ user.name }}
        </li>
    </ul>
    

    我们将会获得如下好处:

    • 过滤后的列表只会在 users 数组发生相关变化时才被重新运算,过滤更高效。
    • 使用 v-for=“user in activeUsers” 之后,我们在渲染的时候只遍历活跃用户,渲染更高效。
    • 解耦渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。

2、key的作用

key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素

  • 用 key 管理可复用的元素,每次切换时,输入框都将被重新渲染。

    <template v-if="loginType === 'username'">
        <label>Username</label>
        <input placeholder="Enter your username" key="username-input">
    </template>
    <template v-else>
        <label>Email</label>
        <input placeholder="Enter your email address" key="email-input">
    </template>
    
  • 触发过渡,完整地触发组件的生命周期钩子

    <transition>
        <span :key="text">{{ text }}</span>
    </transition>
    

    当 text 发生改变时, 总是会被替换而不是被修改,因此会触发过渡。

    同理,key属性被用在组件上时,当key改变时会引起新组件的创建和原有组件的删除,此时组件的生命周期钩子就会被触发。

  • key 列表渲染 (参考文档

    推荐使用数据的唯一标识作为key,比如id,身份证号,手机号等等,通常这些数据由后端提供。

    后续操作不破坏原来数据顺序的话,使用index作为key也没有任何问题。

3、动态组件

  • 不同组件之间进行动态切换,比如在一个多标签的界面里,可以通过 Vue 的 元素加一个特殊的 is attribute 来实现。
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
  • 结合 keep-alive 使用, 可以实现组件的缓存:
<keep-alive>
    <component v-bind:is="currentTabComponent" class="tab">
    </component>
</keep-alive>

四、API

1、model

允许一个自定义组件在使用 v-model 时定制 propevent。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。

<template>
    <div>
        <div class="mycheck-box">{{ value }}-{{ checked }}</div>
        <input v-model="email" placeholder="Enter your email address" />
    </div>
</template>
<script>
export default {
    name: 'MyCheck',
    model: {
        prop: 'checked',
        event: 'change',
    },
    props: {
        // this allows using the `value` prop for a different purpose
        value: String,
        // use `checked` as the prop which take the place of `value`
        checked: {
            type: String,
            default: '',
        },
    },
    data() {
        return {
            email: '',
        }
    },
    watch: {
        email(val) {
            this.$emit('change', val)
        },
    },
    created() {
        this.email = this.checked
    },
    methods: {
        checkClick() {
            this.$emit('change', 4)
        },
    },
}
<my-checkbox v-model="foo" value="some value"></my-checkbox>

上述代码相当于:

<my-checkbox
  :checked="foo"
  @change="val => { foo = val }"
  value="some value">
</my-checkbox>

五、路由

对于大多数单页面应用,都推荐使用官方支持的 vue-router 库。更多细节可以移步 vue-router 文档。

以下是我们项目中实际用到的技术点:

1、导航守卫

正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。

  • 全局前置守卫
    你可以使用 router.beforeEach 注册一个全局前置守卫:

    const router = new VueRouter({
        mode: 'history',
        base: pkg.path,
        routes,
    })
    // 结合中间件使用,在跳转路由前通过中间件拦截,常用于用户登录,角色权限验证等逻辑
    router.beforeEach((to, from, next) => {
        console.log('to:', to, 'from', from)
        if (!to.meta.middleware) {
            return next()
        }
        const middleware = to.meta.middleware
    
        const context = {
            to,
            from,
            next,
            store,
        }
    
        return middleware[0]({
            ...context,
            next: middlewarePipeline(context, middleware, 1),
        })
    })
    
  • 全局后置钩子
    你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

    router.afterEach((to, from) => {
        // 调整滚动位置
        if (to.meta.noCache == true) {
            window.scrollTo(0, -100)
        }
        // 全局修改浏览器title
        if (to.meta.title) {
            document.title = to.meta.title
        }
    })
    
  • 组件内的守卫

    const Foo = {
        template: `...`,
        beforeRouteEnter(to, from, next) {
            // 在渲染该组件的对应路由被 confirm 前调用
            // 不!能!获取组件实例 `this`
            // 因为当守卫执行前,组件实例还没被创建
        },
        beforeRouteLeave(to, from, next) {
            // 导航离开该组件的对应路由时调用
            // 可以访问组件实例 `this`
        }
    }
    

    我们一般获取数据都是在 vue 的 createmountedactivated 三个钩子函数内进行,但也存在一些特殊情况,需要在组件渲染前用到一些数据判断逻辑,这时候们就可以通过 beforeRouteEnter 进行数据获取,实例:

    beforeRouteEnter(to, from, next) {
        mainRequest
            .getToken({ code: ' ' })
            .then((res) => {
                if (res.error_no == 0) {
                    // Toast('cuow')
                    next((vm) => vm.setData(null, res))
                }
            })
            .catch((err) => {
                Toast('cuow')
                next()
            })
    },
    

2、路由原信息

定义路由的时候可以配置 meta 字段

{
    path: '/vue-example',
    name: 'VueExample',
    component: '...'
    meta: {
        title: 'Vue 基础', // 导航标题
        middleware: [auth], // 中间件登录验证
        noCache: true, // 是否开启 keep-alive
    },
}

3、滚动行为

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

注意: 这个功能只在支持 history.pushState 的浏览器中可用。

当创建一个 Router 实例,你可以提供一个 scrollBehavior 方法:

const router = new VueRouter({
    routes: [...],
    scrollBehavior (to, from, savedPosition) {
        if (savedPosition) {
            return savedPosition
        } else {
            return { x: 0, y: 0 }
        }
    }
})

技术分享

一、Vant 组件库

1、覆盖默认样式

通过自定义样式类或者直接重写组件样式类名时来覆盖默认样式时,一定要使用 scoped,保证样式独立,参考下面的示例:

<template>
  <van-button class="my-button">按钮</van-button>
</template>

<style scoped>
    /** 覆盖 Button 最外层元素的样式 */
    .my-button {
        width: 200px;
    }

    /** 覆盖 Button 内部子元素的样式 */
    .my-button .van-button__text {
        color: red;
    }
    /* 重写 */
    .van-button--normal {
        font-size: 30px;
    }

    /* 某些样式直接覆盖不了,可以通过keep 进行穿透 */
    /keep/.van-picker-column {
        ul li {
            font-size: 30px;
        }
    }
}
</style>

2、桌面端适配

Vant 是一个面向移动端的组件库,因此默认只适配了移动端设备,这意味着组件只监听了移动端的 touch 事件,没有监听桌面端的 mouse 事件。

如果你需要在桌面端使用 Vant,可以引入我们提供的 @vant/touch-emulator,这个库会在桌面端自动将 mouse 事件转换成对应的 touch 事件,使得组件能够在桌面端使用。

# 安装模块
npm i @vant/touch-emulator -S
或
yarn add  @vant/touch-emulator
// 在main.js 引入模块后自动生效
import '@vant/touch-emulator'

3、安全区适配

iPhone X 等机型底部存在底部指示条,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行安全区适配。Vant 中部分组件提供了 safe-area-inset-top 或 safe-area-inset-bottom 属性,设置该属性后,即可在对应的机型上开启适配,如下示例:

<!-- 在 head 标签中添加 meta 标签,并设置 viewport-fit=cover 值 -->
<meta
  name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
/>

<!-- 开启顶部安全区适配 -->
<van-nav-bar safe-area-inset-top />

<!-- 开启底部安全区适配 -->
<van-number-keyboard safe-area-inset-bottom />

自定义组件适配安全区域,示例:

    .main-content {
        padding-bottom: constant(safe-area-inset-bottom);
        padding-bottom: env(safe-area-inset-bottom);
    }

4、浏览器适配

Vant 默认使用 px 作为样式单位,设计稿的尺寸是375,所以为了适配vant和自定义样式的统一性,统一通过PostCSS 对样式单位进行转换。

  • 移动端app h5 项目,使用的是 Viewport 布局,配置如下:
      // postcss.config.js
      module.exports = {
          plugins: {
              'postcss-px-to-viewport': {
                  viewportWidth: 375,
              },
          }
      }
    
  • 企业微信 h5 项目,使用的是 Rem 布局适配,配置如下:
      // postcss.config.js - postcss-pxtorem
      module.exports = {
          plugins: {
              'postcss-pxtorem': {
                  rootValue: 37.5,
              }
          }
      }
    
  • 如果设计稿的尺寸不是 375,而是 750 或其他大小,可以将 rootValue 配置调整为:
      // postcss.config.js
      module.exports = {
          plugins: {
              // postcss-pxtorem 插件的版本需要 >= 5.0.0
              'postcss-pxtorem': {
              rootValue({ file }) {
                  return file.indexOf('vant') !== -1 ? 37.5 : 75;
              },
              propList: ['*'],
              },
          },
      }
    

二、moment 时间过滤器

JavaScript 日期处理类库,用于日期格式转换的,Moment 被设计为在浏览器和 Node.js 中都能工作。

详细内容可参考官方文档

Vue 项目安装

npm install moment
或
yarn add moment

Vue 引用

import moment from 'moment'

使用示例:

  • 日期格式化
    /**
    * @description:  格式化日期 YYYYMMDD 年月日
    * @param {*} value
    * @param {*} formater
    * @return {*}
    * @author: lrp
    * @Date: 2022-06-20 15:26:22
    */
    export function shortTime(value,formater = 'YYYYMMDD'){
        return moment(value).format(formater)
    }
    
  • 获取周日期
    /**
     * @description: 获取本周日期 周一~周天,并格式化日期
     * @param {*} formater
     * @return {* [start,end]}返回本周日期区间
     * @author: lrp
     * @Date: 2022-06-27 09:51:11
     */
    export function weekDate(formater = 'YYYYMMDD'){
        moment.locale('zh-cn',{
            week:{
                dow:1
            }
        })
        let start = moment().startOf('week').format(formater)
        let end = moment().endOf('week').format(formater)
        // let start=  moment().isoWeekday(1).format(formater); // 星期一
        // let end = moment().isoWeekday(7).format(formater); // 星期日
        return [start,end]
    }
    

问题记录

一、UI 异常问题

1、ios 12 iframe无法滚动

商城项目用iframe展示法律条款及一些协议,页面里采用flex布局,分三块,上面标题、中间iframe展示,底部下一步操作按钮。

经测试,各个手机都很正常,唯有一个ios12的手机,iframe无法滚动。

解决问题关键点在于:-webkit-overflow-scrolling: touch

参考:https://www.jianshu.com/p/af837464ba56

具体解决方案:

.iframe-detail-box {
    touch-action: none;
    .iframe-detail-body-box {
        height: 100%;
        -webkit-overflow-scrolling: touch;
        overflow-y: scroll;
        iframe {
            height: 100%;
            width: 1px;
            min-width: 100%;
            *width: 100%;
        }
    }
}
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值