export default用法vue_vue.js 使用技巧总结

前言

写 vue 有很长一段时间了 , 在使用过程中会遇到各种场景的挑战 , 本文记录了我在使用 vue 中发现的一些好的实践,希望能够帮助到大家。

vue.js 使用技巧

覆盖 element-ui 样式

只覆盖某个组件内的 element-ui 样式,而不影响全局。

命名空间

由于 element-ui 的样式我们是在全局引入的,所以你想在某个页面里面覆盖它的样式就不能加 scoped,但你又想只覆盖这个页面的 element 样式,你就可在它的父级加一个 class,用命名空间来解决问题。

.article-page {
  /* 你的命名空间 */
  .el-tag {
    /* element-ui 元素*/
    margin-right: 0px;
  }
}

样式穿透

vue工程本来就比较庞大复杂,一个页面很可能会加载很多的组件,难免名字相同,除非你的项目有很严格的命名空间管理,否则我更推荐使用深度作用选择器的方式来覆盖样式。

 <el-dialog>
       <el-table style="margin-left:30px"></el-table>
   </el-dialog>
   <style scoped>
       .el-dialog >>> .el-table{
           margin-left:20px!important;
           /*这里的margin-left:20px;将会覆盖内联样式*/
        }
   </style>

注意:如果你使用了一些预处理的东西,如sass你可以通过/deep/来代替>>>实现想要的效果。

7239bb103ddf2848cdb5ceba902a3cfb.png

sass 和 js 之间变量共享

使用场景:动态换肤,需要将用户选择的主题传递给 css,初始化时 css 又需要将默认主题色传递给 js

js 将变量传递给 sass

可以通过 vue 内联标签的方式实现,或者使用 css var(),用 less 的话 modifyVars ,等等方案都能实现 jscss 的变量传递。

<div :style="{'background-color':color}" ></div>

sass 将变量给 js

js 该怎么获取 sass 中的变量呢,通过 css-modules:export 来实现

// var.scss
$theme: blue;

:export {
  theme: $theme;
}

css-modules:export

// test.js
import variables from '@/styles/var.scss'
console.log(variables.theme) // blue

调试 template 代码

在vue.js开发过程中,我们可以使用DevTools来完成代码调试工作,但有时候会遇到template模板渲染时JavaScript变量出错的问题。

我们可以使用console.log的方式来调试,推荐使用vue原型挂载的方式,毕竟不会只有一个组件需要调试。

// main.js
Vue.prototype.$log = window.console.log;

// .vue
<div>{{$log(message)}}</div>

当然我们也可以用||运算符,这样既能调试,也不会影响渲染。

<div>{{ $log(message) || message }}</div>

attrs 和 listeners 二次封装组件

我们平时写业务的时候免不了需要对一些第三方组件进行二次封装,当我们去二次封装别人组件时,可能别人组件上有很多属性,我们不想再次重写一遍。

<template>
  <div>
    <el-button v-bind="$attrs">确定</el-button>
  <div>
</template>
 
// 父组件使用
<my-button type='primary' size='mini'/>

另外,防止小伙伴们不熟悉 attrs 和 listeners,这里也做个简单介绍。

// 父组件
<home title="这是标题" @change="change" width="80" height="80" imgUrl="imgUrl"/>

// 子组件
mounted() {
  console.log(this.$attrs) //{title: "这是标题", width: "80", height: "80", imgUrl: "imgUrl"}
  console.log(this.$listeners) //即可拿到 change 事件
}

.sync 语法糖

当你有需要在子组件修改父组件值的时候这个方法很好用,它的实现机制和v-model是一样的。

af0c8e9d38713fc913e43bcdca70b7b6.png

热更新速度优化

热更新是我们每天打交道的东西,热更新的快慢,直接影响我们的开发效率。

首先,路由懒加载非常非常不适用于开发环境,会严重热更新速度。

区分开发环境

最早的方案是区分开发环境与生产环境,在路由文件夹下分别新建两个文件:

开发环境:

module.exports = file => require("@iews/" + file + ".vue").default;

生成环境:

module.exports = file => () => import("@iews/" + file + ".vue");

这样组件在开发环境下就是非懒加载,生产环境下就是懒加载的了。

dynamic-import-node

babeldynamic-import-node是一种新的解决方案,其实思路都是一样的,只不过不需要开发者在需要建两份路由。

"env": {
    "development": {
      "plugins": ["dynamic-import-node"]
    }
 }

最后,除了路由懒加载外,还应排查以下几点问题:

  1. 没有合理使用 souce map
  2. 开发环境不要压缩代码,提取 css babel polyfill

利用 object.freeze 提升性能

比方我们需要渲染一个非常大的数组对象,例如用户列表,对象列表,文章列表等等。

export default {
  data: () => ({
    users: {}
  }),
  async created() {
    const users = await axios.get("/api/users");
    this.users = users;
  }
}

vue 会将 data 对象中的所有的属性加入到 vue 的响应式系统中,当这些属性的值发生改变时,视图将会产生 响应,若对象的体积比较大,会消耗很多浏览器解析时间。

所以我们可以通过减少数据的响应式转换来提供前端的性能。

export default {
  data: () => ({
    users: {}
  }),
  async created() {
    const users = await axios.get("/api/users");
    this.users = Object.freeze(users);
  }
}

始终在 v-for 中使用 :key

加上key可以最大化的利用节点,减少性能消耗,至于原因,需要大家理解diff的过程,并且要对virtual dom有个了解。

<!-- 不好的做法-->
<div v-for='product in products'>  </div>

<!-- 好的做法 -->
<div v-for='product in products' :key='product.id'>

注意:在工作中,发现很多人直接用index作为key,好像几乎没遇到过什么问题。确实,index作为key,在表现上确实几乎没有问题,但它主要有两点不足。

  • index 作为 key ,其实就等于不加 key
  • index 作为 key,只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出(这是 vue 官网的说明)

不要在同个元素上同时使用 v-if 和 v-for 指令

永远不要把v-ifv-for同时用在同一个元素上,v-forv-if优先级高,所以嵌套使用的的话,每次v-for都会执行v-if,造成不必要的计算,影响性能,尤其是当之需要渲染很小一部分的时候。

<ul>
 <li 
  v-for='product in products' 
  :key='product.id' 
  v-if='product.price < 50'
 >
  {{ product.name }}
 <>
</ul>

为避免上述问题,可以使用过滤方法,方式如下:

<ul>
  <li v-for='products in productsUnderPrice(50)' :key='product._id' >
    {{ product.name }}
  <>
</ul>

<script>
  export default {
    data () {
      return {
        products: []
      }
    },
    methods: {
      productsUnderPrice (price) {
        return this.products.filter(product => product.price < price)
      }
    }
  }
</script>

利用 require.context 简化 vux

require.contextwebpack用来管理依赖的一个函数,可以利用它实现无限制的多级模块导入。

首先看我的store目录结构:

e0c5974a6012024f3c6865953e3d2a46.png

每一个文件都是一个模块,所以在store/index.js里面可以这样写 :

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
const modulesFiles = require.context('./modules', true, /.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // set './app.js' => 'app'
  const moduleName = modulePath.replace(/^./(.*).w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})

const store = new Vuex.Store({
  modules
})

export default store

使用 render 渲染组件

模板语法在大多数情况下很好,但有时候不是这样,所以这个时候渲染函数就会发挥作用。

// 模板语法
<template>
  <div>
    <div v-if="level === 1"> <slot></slot> </div>
    <p v-else-if="level === 2"> <slot></slot> </p>
    <h1 v-else-if="level === 3"> <slot></slot> </h1>
    <h2 v-else-if="level === 4"> <slot></slot> </h2>
    <strong v-else-if="level === 5"> <slot></slot> </stong>
    <textarea v-else-if="level === 6"> <slot></slot> </textarea>
  </div>
</template>

// render 渲染函数
<script>
  export default {
    name:  hehe ,
    render(h) {
      const tag = [ div ,  p ,  strong ,  h1 ,  h2 ,  textarea ][this.level-1]
      return h(tag, this.$slots.default)
    },
    props: {
      level: {  type: Number,  required: true  } 
    }
  }
</script>

简单清晰很多!简单来说,这样代码精简很多。

注意:如果组件中有模板语法,那么 render 函数就会无效,并且如果使用了 render 函数,那么 vue 中自带的一些指令就不在生效了,包括 v-if , v-for 和 v-model ,需要我们自己实现。

render适合复杂逻辑,template适合逻辑简单;

vue原理中,template通过编译生成AST,在由AST生成render函数,最后生成虚拟DOM,所以说render的性能会更高。

我们有必要了解vue中的一些基本概念,最后上一张图,这张图从宏观上展现了vue整体流程。

0ae14be203c1e3055061f9c5081286ef.png

jsx 语法糖

复杂的render函数书写异常痛苦,好在官方提供了一个Babel插件,在vuerender函数中也可以直接使用jsx语法。

注意:如果你使用的是 vue-cli 3.x 创建的项目,那么不需要任何配置,直接就可以使用 jsx。

//模板语法
  <h1 v-if="level === 1">
    <slot></slot>
  </h1>
  <h2 v-else-if="level === 2">
    <slot></slot>
  </h2>
  <h3 v-else-if="level === 3">
    <slot></slot>
  </h3>

//jsx
const App = {
  render() {
    const tag = `h${this.level}`
    return <tag>{this.$slots.default}</tag>
  }
}

函数式组件

如果我们所需的组件比较简单,没有管理任何状态,也没有生命周期方法,它只是接受一些prop的函数,在这样的场景下,我们可以使用函数式组件

render

<script>
export default{
  functional: true, // 添加属性functional: true,表示该组件为函数式组件
  // Props 是可选的
  props: {
    // ...
  },
  // 为了弥补缺少的实例
  // 提供第二个参数作为上下文
  render: function (createElement, context) {
    // ...
  },
}
</script>

functional

<template functional>
    <div v-for="(item,index) in props.arr">{{item}}</div>
</template>

context 描述

函数式组件是无状态的,也没有this上下文,没有data等属性,所以所有数据都是由render函数的第二个参数context获得的

  • props:提供所有 prop 的对象
  • data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
  • children: VNode 子节点的数组
  • parent:对父组件的引用
  • slots: 一个函数,返回了包含所有插槽的对象
  • scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
  • listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
  • injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。

动态切换组件

如果我们打算根据状态引用不同的组件,比如tab页,那么就会涉及到组件动态加载

<component :is="currentTabComponent"></component>

但是这样每次组件都会重新加载,会消耗大量性能,所以<keep-alive>就起到了作用

<keep-alive>
  <component :is="currentTabComponent"></component>
</keep-alive>
作者:海洋里的魔鬼鱼
链接: https:// juejin.cn/post/68500372 71803428872
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

a8953298d7eee816403a6e64ec21fd3c.gif
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值