Vue 开发经验小记(持续更新)

2022-04-06 13:32:32 浏览数 (1)

使用 vue 开发过程中遇到的问题或知识点总结,持续更新中…

最近更新:2019-11-29

1.国际化

国际化插件:vue-i18n

2.强制换行与禁止换行

让多行内容显示一行,多余的用...表示

代码语言:javascript复制
white-space : nowrap
overflow: hidden
text-overflow : ellipsis

内容超过宽度时强制换行

代码语言:javascript复制
overflow: hidden;
word-wrap:break-word;
overflow-wrap: break-word;

注:CSS3中将 <' word-wrap '> 改名为 <' overflow-wrap '>,用时最好两个都写上

3.显示宽高相等的图片,宽度为屏幕宽度,高度与宽度相等

代码语言:javascript复制
<div class="image-header">
  <img :src="food.image"/>
</div>

.image-header
    position: relative
    width:100%
    height: 0
    padding-top : 100%
    img
        position: absolute
        left: 0
        top: 0
        width: 100%
        height: 100%

重点是父元素的height设为0,padding-top设为100%

4.转换时间的工具类

代码语言:javascript复制
/**
 * Created by solo on 2018/6/6.
 */

export function formatDatetime(date, fmt) {
  if(/(y )/.test(fmt)){
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() "").substr(4-RegExp.$1.length))
  }
  let obj = {
    "M ": date.getMonth()   1,
    "d ": date.getDay(),
    "h ": date.getHours(),
    "m ": date.getMinutes(),
    "s ": date.getSeconds()
  }
  for(let key in obj){
    if(new RegExp((<span class="hljs-subst">${key}</span>)).test(fmt)){
      let str = obj[key]   ''
      fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str))
    }
  }
  return fmt
}
function padLeftZero(str) {
  return ("00"   str).substr(str.length)
}

使用

代码语言:javascript复制
let date = new Date(timestamp)
let fmtDate =  formatDatetime(date, 'yyyy-MM-dd hh:mm')

也可以使用第三方的库: moment.jsdayjs

5.给组件绑定原生事件

代码语言:javascript复制
<custom @click.native='handleClick'></custom>

只需要在@click后面加上.native就可以直接处理原生点击事件了

6. vue中组件间传值

6.1 父子组件间传值
  • 父组件给子组件传值,直接通过props传值
代码语言:javascript复制
<custom content="hello world"></custom>
  • 子组件给父组件传值,通过 emit发送事件
代码语言:javascript复制
this.$emit('chooseType', type)

父组件接收事件:

代码语言:javascript复制
<custom content="hello world" @chooseType="handleType"></custom>
6.2 非父子组件传值

主要通过事件总线传值

在根节点给 Vue 挂载一个空的 Vue 对象

代码语言:javascript复制
Vue.prototype.bus = new Vue();

需要发送事件的组件里

代码语言:javascript复制
this.bus.$emit("change", params)

接收事件的组件

代码语言:javascript复制
this.bus.$on("change", (msg) => {
    //do yourself work
})

7. 动态组件

动态切换显示的组件

代码语言:javascript复制
<component :is='type'></component>

data(){
    components:{
        component-one,
        component-two
    }
    return{
        type: 'component-one'
    }
}

<component> 是vue官方提供的标签,通过更改 is 指向的子组件名来动态切换组件。

8. v-once 指令

只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

代码语言:javascript复制
<!-- 单个元素 -->
<span v-once>This will never change: {{msg}}</span>
<!-- 有子元素 -->
<div v-once>
  <h1>comment</h1>
  <p>{{msg}}</p>
</div>
<!-- 组件 -->
<my-component v-once :comment="msg"></my-component>
<!-- `v-for` 指令-->
<ul>
  <li v-for="i in list" v-once>{{i}}</li>
</ul>

9.过渡和动画

9.1 过渡
代码语言:javascript复制
.fade-enter-active, .fade-leave-active{
    transition: opacity 2s
}
.fade-enter, .fade-leave-to{
    opacity: 0
}
9.2 动画结合 Animate.css
代码语言:javascript复制
//引入 animate.css
<link rel="stylesheet" type="text/css" href="animate.css">

//布局
<transition enter-active-class="animated bounce" leave-active-class="animated shake">
    <p v-if="show">hello world</p>
</transition>
<button @click='toggleShow'>toggle</button>

要定义 enter-active-classleave-active-class 的类名,且必须有 animated,想要什么动画效果就写在第二个位置上

解决第一次显示没有动画的bug

代码语言:javascript复制
<transition 
    appear 
    enter-active-class="animated bounce" 
    leave-active-class="animated shake" 
    appear-active-class="animated bounce">
    <p v-if="show">hello world</p>
</transition>

<transition> 上添加 appearappear-active-class 即可。

9.3 同时使用过渡和动画
代码语言:javascript复制
<transition 
    name="fade"
    type='transition'
    appear 
    enter-active-class="animated bounce fade-enter-active" 
    leave-active-class="animated shake fade-leave-active" 
    appear-active-class="animated bounce">
    <p v-if="show">hello world</p>
</transition>

enter-active-classleave-active-class 加上相应的类名 fade-enter-activefade-leave-active ,然后在样式中定义过渡效果即可。

代码语言:javascript复制
.fade-enter-active, .fade-leave-active{
    transition: opacity 2s
}
.fade-enter, .fade-leave-to{
    opacity: 0
}

动画执行的总时长是根据动画还是过渡来定呢?可以手动指定:

代码语言:javascript复制
//指定整体动画时间为过渡动画时间
type='transition'

还可以自己指定动画总时长:

代码语言:javascript复制
//指定动画时长为10秒
:duration="10000"

//分别指定进场时长5秒和出场动画时长10秒
:duration="{enter: 5000, leave: 10000}"
9.4 多个组件和元素的过渡
  • 多个元素过渡
代码语言:javascript复制
<div id="app">
    <transition name="fade" mode="out-in">
        <div v-if="show" key="hello">Hello world</div>
        <div v-else key="bye">Bye world</div>
    </transition>
    <button @click="toggleShow">Add</button>
</div>

需要给元素加 key, 防止vue复用元素导致没有动画效果。

可以指定切换模式,mode="out-in":先出后进,mode="in-out":先进后出

  • 多个组件过渡跟多个元素过渡类似
9.5 vue中列表过渡

使用 transition-group 属性

代码语言:javascript复制
<div id="app">
    <transition-group name="fade">
        <div v-for="item in list" :key="item.id">
            {{item.title}}
        </div>
    </transition-group>
    <button @click="add2List">Add</button>
</div>


<style type="text/css" >
    .fade-enter-active, .fade-leave-active{
        transition: opacity 2s
    }
    .fade-enter, .fade-leave-to{
        opacity: 0
    }
</style>

10. img 标签的 src 动态绑定

1)路径固定的图片

路径前加 require()

代码语言:javascript复制
<img :src="bookingManageImg" slot="icon"/>

bookingManageImg(){
    return this.selectedTab === "bookingManage" ?     require('../assets/manage_focus.png') : require('../assets/manage_normal.png')
},
2)for 循环里图片的路径不固定

如果在循环里还直接用 require() 的话,webpack 会将图片来当做模块来用,因为是动态加载的,所以 url-loader 将无法解析图片地址,所以会报错找不到模块。

解决办法是采用拼接的方式: require('../assets/icons/' item.icon '.png'),对象里只存图片的名字,图片路径是固定的,所以直接用字符串写上去。

list 的数据格式:

代码语言:javascript复制
const list = [
    {
      name: "美食",
      icon: "food"
    },
    {
      name: "电影",
      icon: "movie"
    },
]

布局文件:

代码语言:javascript复制
<div class="item" v-for="item in list">
  <img :src="require('../assets/icons/'   item.icon   '.png')" class="icon">
  <div class="name">{{item.name}}</div>
</div>

11. vuex 在页面刷新后状态丢失解决办法

刷新页面后,存在 vuex 的数据会丢失,给调试带来不便。把用户的登录信息放到 sessionStorage 中避免丢失。

代码语言:javascript复制
const USER_INFO = "userInfo";

export default new Vuex.Store({
  state: {
    userInfo: JSON.parse(sessionStorage.getItem(USER_INFO))
  },
  mutations: {
    setUserInfo(state, userInfo){
      //存储到 sessionStorage 中以防刷新页面后状态丢失
      sessionStorage.setItem(USER_INFO, JSON.stringify(userInfo));
      state.userInfo = userInfo
    }
  }
}

12. 返回记住滚动条位置

详细解析见文章:Vue 返回记住滚动条位置详解

13. 修改页面 Title

首先在 router.js 里,每个路由加上 meta ,设置 title

代码语言:javascript复制
routes: [
  {
    path: '/login',
    name: 'login',
    component: Login,
    meta:{
      title:'登录'
    }
  },
  {
    path: '/home',
    name: 'home',
    component: Home,
    children: [],
    meta:{
      title:'主页'
    }
  }
]

然后在 main.js 里通过前置路由动态修改 title

代码语言:javascript复制
router.beforeEach((to, from, next) => {
  /* 路由发生变化修改页面title */
  if (to.meta.title) {
    document.title = to.meta.title;
  }
  next();
})

14. 打包时启用 Gzip 压缩

先安装 webpack 插件

代码语言:javascript复制
npm install --save-dev compression-webpack-plugin

再在 vue.config.js 里添加如下代码:

代码语言:javascript复制
const CompressionPlugin = require("compression-webpack-plugin")

module.exports = {
  // 基本路径
  baseUrl: './',
  // 输出文件目录
  outputDir: 'dist',
  // 启用 Gzip 压缩
    configureWebpack: () => {
    module.exports = {
      configureWebpack:config=>{
        if(progress.env.NODE_ENV === 'production'){
          return{
            plugins: [
          <span class="hljs-keyword">new</span> CompressionPlugin({
            <span class="hljs-attr">test</span>:<span class="hljs-regexp">/.js$|.html$|.css/</span>, <span class="hljs-comment">//匹配文件名</span>
            threshold: <span class="hljs-number">10240</span>,<span class="hljs-comment">//对超过10k的数据压缩</span>
            deleteOriginalAssets: <span class="hljs-literal">false</span> <span class="hljs-comment">//不删除源文件</span>
          })
        ]
      }
    }
  },
}

  },
}

Vue CLI 3 默认没有 vue.config.js ,在根目录新建一个就好,位置跟 package.json 同级。

15. vue 与 安卓原生应用通信

我有一篇专门讲解vue与安卓双向通信的文章:

Android webview 与 js(Vue) 交互

16. 如何在样式中使用 scss 的声明的全局变量

sass 声明的变量如:

代码语言:javascript复制
$color-primary: #409EFF;
$color-success: #67C23A;
$color-warning: #E6A23C;
$color-danger: #F56C6C;
$color-info: #909399;

普通的引用方法为

代码语言:javascript复制
<style scoped lang="scss">
  @import "../../public/css/index";
  .home {
    color: $color-primary;
  }
</style>

需要先在要使用的文件中引入声明的文件,然后才能使用。

这样比较麻烦,代码冗余。可以使用更优雅的方式:sass-resources-loader

使用 sass-resources-loader 需要两步:

其他环境的详细配置说明见 sass-resources-loader 官网

配置完之后,就可以在任意文件里使用 sass 声明的变量啦。

17. 子组件中改变父组件通过 props 传递过来的属性

官方是不推荐子组件直接改变父组件传递过来的属性的,如果你这么做了,会有警告。

但有时的确是需要在子组件中改变父组件的属性,因为省事啊……比如子组件中有 Dialog,Dialog 的显示与隐藏要通过父组件控制,同时子组件关闭了 Dialog 要同步更新父组件中属性值。

当然有很多 "正确" 的方式可以做到,比如 vuex,比如用父子组件的通信,子组件改变值了发个通知通知父组件更新对应的值。

但是,上面两种方法都比较麻烦。我就想在父组件中给子组件传递个变量,子组件改变它的值了,父组件中的变量也会自动更新。

这就用到一个 "漏洞",把要传递的值封装成一个对象,改变对象中的属性值,就不会出现警告。因为对象还是原来的对象,只是里面的值变了。

父组件如下。注意 data 中的 visible: {value: false} 是个对象,不能写成 visible: false,会出现警告。

代码语言:javascript复制
<template>
  <child :visible="visible"/>
</template>

<script>
  export default {
    components: {
        child
    },
    data(){
        return{
            visible: {value: false}
        }
    }
  }
</script>

子组件如下:

代码语言:javascript复制
<el-dialog :visible.sync="visible.value">

当子组件改变值时改变的是 visible 对象中的 value 属性。这样就可以通过子组件直接改变父组件的值了。

18. 九宫格的实现

实现类似的九宫格代码:

代码语言:javascript复制
<template>
  <div class="view-home">
    <div class="category-wrapper">
      <div class="category-name">分类一</div>
      <div class="icons">
        <div class="item" v-for="item in list">
          <img :src="require('../assets/icons/'   item.icon   '.png')" class="icon">
          <div class="name">{{item.name}}</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  const LIST = [
    {name: "电影",icon: "movie"},
    {name: "美食",icon: "food"},
    {name: "美发",icon: "hair"},
    {name: "周边游",icon: "around"},
    {name: "酒店",icon: "hotel"},
    {name: '代购',con: "dg"}
  ];
  export default {
    components: {},
    data() {
      return {
        list: ICON_LIST,
      }
    },
  }
</script>
<style scoped lang="scss">
  .view-home {
    display: flex;
    flex-direction: column;
    width: 100%;
    padding: px2rem(10);
    box-sizing: border-box;
.category-wrapper {
  width: 100%;
  display: flex;
  flex-direction: column;
  background-color: white;

  .category-name {
    font-size: $font-size-normal;
    color: $text-main;
    padding: px2rem(12);
    border-bottom: px2rem(1) solid $border-third;
  }

  .icons {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;

    .item {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      width: 25%;
      padding: px2rem(10) 0;

      .icon {
        width: px2rem(40);
        height: px2rem(40);
      }

      .name {
        font-size: $font-size-small;
        color: $text-normal;
        margin-top: px2rem(10);
      }
    }
  }
}

  }
</style>

重点:

19. 超出宽度横向滑动

当子组件的宽度超过父组件,实现横向滑动。

父组件可以是整个屏幕的根元素,也可以是某个特定的元素。只要设置好 css 即可。

设父元素的 class=parent,子元素的 class=child

代码语言:javascript复制
.parent{
    //其他样式省略,只列出控制横向滑动必须的代码
    display: flex;
    overflow-x: auto;
    overflow-y: hidden; 
    
.child{
    // 其他样式省略,只列出控制横向滑动必须的代码
    //这句话的意思是不会被压缩大小
    flex-shrink: 0;
}

}

在你的 css 代码中加上这几行,就可以实现横向滑动啦。

20. 只显示 n 行,多余的用省略号表示

经常有需求是只显示两行或三行,多余的用省略号表示。

适用范围: 因使用了WebKit的CSS扩展属性,该方法适用于WebKit浏览器及移动端;

注: -webkit-line-clamp用来限制在一个块元素显示的文本的行数。 为了实现该效果,它需要组合其他的WebKit属性。 常见结合属性: display: -webkit-box: 必须结合的属性 ,将对象作为弹性伸缩盒子模型显示。 -webkit-box-orient: 必须结合的属性,设置或检索伸缩盒对象的子元素的排列方式。

代码语言:javascript复制
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;

21. flex 布局中,单个元素靠右对齐

如下图,姓名、性别、评论图标这一行有三个元素,是 flex 布局,前两个元素靠左,评论图标靠右。

已知父元素的布局为

代码语言:javascript复制
display: flex;
flex-direction: row;
align-items: center;

实现起来有三种方法:

给姓名和性别两个元素再加一层 div, 并把这个 div 设置 。缺点是多了层嵌套,有点麻烦。

给评论图标这个元素设置

给评论图标这个元素设置

后两种方法都比较简单,推荐。

0 人点赞