Vue 開發經驗小記(持續更新)

solocoder發表於2019-03-05

使用 vue 開發過程中遇到的問題或知識點總結,持續更新中…

最近更新:2019-11-29

1.國際化

國際化外掛:vue-i18n

2.強制換行與禁止換行

讓多行內容顯示一行,多餘的用...表示

white-space : nowrap
overflow: hidden
text-overflow : ellipsis
複製程式碼

內容超過寬度時強制換行

overflow: hidden;
word-wrap:break-word;
overflow-wrap: break-word;
複製程式碼

注:CSS3中將 <' word-wrap '> 改名為 <' overflow-wrap '>,用時最好兩個都寫上

3.顯示寬高相等的圖片,寬度為螢幕寬度,高度與寬度相等

<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.轉換時間的工具類

/**
 * 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(`(${key})`).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)
}
複製程式碼

使用

let date = new Date(timestamp)
let fmtDate =  formatDatetime(date, 'yyyy-MM-dd hh:mm')
複製程式碼

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

5.給元件繫結原生事件

<custom @click.native='handleClick'></custom>
複製程式碼

只需要在@click後面加上.native就可以直接處理原生點選事件了

6. vue中元件間傳值

6.1 父子元件間傳值

  • 父元件給子元件傳值,直接通過props傳值
<custom content="hello world"></custom>
複製程式碼
  • 子元件給父元件傳值,通過 emit傳送事件
this.$emit('chooseType', type)
複製程式碼

父元件接收事件:

<custom content="hello world" @chooseType="handleType"></custom>
複製程式碼

6.2 非父子元件傳值

主要通過事件匯流排傳值

在根節點給 Vue 掛載一個空的 Vue 物件

Vue.prototype.bus = new Vue();
複製程式碼

需要傳送事件的元件裡

this.bus.$emit("change", params)
複製程式碼

接收事件的元件

this.bus.$on("change", (msg) => {
    //do yourself work
})
複製程式碼

7. 動態元件

動態切換顯示的元件

<component :is='type'></component>

data(){
	components:{
        component-one,
        component-two
	}
    return{
        type: 'component-one'
    }
}
複製程式碼

<component> 是vue官方提供的標籤,通過更改 is 指向的子元件名來動態切換元件。

8. v-once 指令

只渲染元素和元件一次。隨後的重新渲染,元素/元件及其所有的子節點將被視為靜態內容並跳過。這可以用於優化更新效能。

<!-- 單個元素 -->
<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 過渡

.fade-enter-active, .fade-leave-active{
	transition: opacity 2s
}
.fade-enter, .fade-leave-to{
	opacity: 0
}
複製程式碼

9.2 動畫結合 Animate.css

//引入 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

<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 同時使用過渡和動畫

<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 ,然後在樣式中定義過渡效果即可。

.fade-enter-active, .fade-leave-active{
	transition: opacity 2s
}
.fade-enter, .fade-leave-to{
	opacity: 0
}
複製程式碼

動畫執行的總時長是根據動畫還是過渡來定呢?可以手動指定:

//指定整體動畫時間為過渡動畫時間
type='transition'
複製程式碼

還可以自己指定動畫總時長:

//指定動畫時長為10秒
:duration="10000"

//分別指定進場時長5秒和出場動畫時長10秒
:duration="{enter: 5000, leave: 10000}"
複製程式碼

9.4 多個元件和元素的過渡

  • 多個元素過渡
<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 屬性

<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()

<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 的資料格式:

const list = [
    {
      name: "美食",
      icon: "food"
    },
    {
      name: "電影",
      icon: "movie"
    },
]
複製程式碼

佈局檔案:

<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 中避免丟失。

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

routes: [
  {
    path: '/login',
    name: 'login',
    component: Login,
    meta:{
      title:'登入'
    }
  },
  {
    path: '/home',
    name: 'home',
    component: Home,
    children: [],
    meta:{
      title:'主頁'
    }
  }
]
複製程式碼

然後在 main.js 裡通過前置路由動態修改 title

router.beforeEach((to, from, next) => {
  /* 路由發生變化修改頁面title */
  if (to.meta.title) {
    document.title = to.meta.title;
  }
  next();
})
複製程式碼

14. 打包時啟用 Gzip 壓縮

先安裝 webpack 外掛

npm install --save-dev compression-webpack-plugin
複製程式碼

再在 vue.config.js 裡新增如下程式碼:

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: [

              new CompressionPlugin({
                test:/\.js$|\.html$|.\css/, //匹配檔名
                threshold: 10240,//對超過10k的資料壓縮
                deleteOriginalAssets: false //不刪除原始檔
              })
            ]
          }
        }
      },
    }
  },
}
複製程式碼

Vue CLI 3 預設沒有 vue.config.js ,在根目錄新建一個就好,位置跟 package.json 同級。

15. vue 與 安卓原生應用通訊

我有一篇專門講解vue與安卓雙向通訊的文章:

Android webview 與 js(Vue) 互動

16. 如何在樣式中使用 scss 的宣告的全域性變數

sass 宣告的變數如:

$color-primary: #409EFF;
$color-success: #67C23A;
$color-warning: #E6A23C;
$color-danger: #F56C6C;
$color-info: #909399;
複製程式碼

普通的引用方法為

<style scoped lang="scss">
  @import "../../public/css/index";
  .home {
    color: $color-primary;
  }
</style>
複製程式碼

需要先在要使用的檔案中引入宣告的檔案,然後才能使用。

這樣比較麻煩,程式碼冗餘。可以使用更優雅的方式:sass-resources-loader

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

  1. 安裝依賴

    npm install sass-resources-loader
    複製程式碼
  2. vue.config.js 裡配置。

    這裡使用的是 Vue-CLI 3. 將程式碼中的 resources 路徑換成自己的路徑即可。

    // vue.config.js
    module.exports = {
      chainWebpack: config => {
        const oneOfsMap = config.module.rule('scss').oneOfs.store
        oneOfsMap.forEach(item => {
          item
            .use('sass-resources-loader')
            .loader('sass-resources-loader')
            .options({
              // Provide path to the file with resources
              resources: './path/to/resources.scss',
    
              // Or array of paths
              resources: ['./path/to/vars.scss', './path/to/mixins.scss']
            })
            .end()
        })
      }
    }
    複製程式碼

其他環境的詳細配置說明見 sass-resources-loader 官網

配置完之後,就可以在任意檔案裡使用 sass 宣告的變數啦。

17. 子元件中改變父元件通過 props 傳遞過來的屬性

官方是不推薦子元件直接改變父元件傳遞過來的屬性的,如果你這麼做了,會有警告。

但有時的確是需要在子元件中改變父元件的屬性,因為省事啊……比如子元件中有 Dialog,Dialog 的顯示與隱藏要通過父元件控制,同時子元件關閉了 Dialog 要同步更新父元件中屬性值。

當然有很多 "正確" 的方式可以做到,比如 vuex,比如用父子元件的通訊,子元件改變值了發個通知通知父元件更新對應的值。

但是,上面兩種方法都比較麻煩。我就想在父元件中給子元件傳遞個變數,子元件改變它的值了,父元件中的變數也會自動更新。

這就用到一個 "漏洞",把要傳遞的值封裝成一個物件,改變物件中的屬性值,就不會出現警告。因為物件還是原來的物件,只是裡面的值變了。

父元件如下。注意 data 中的 visible: {value: false} 是個物件,不能寫成 visible: false,會出現警告。

<template>
  <child :visible="visible"/>
</template>

<script>
  export default {
    components: {
    	child
    },
    data(){
    	return{
    		visible: {value: false}
    	}
    }
  }
</script>
複製程式碼

子元件如下:

<el-dialog :visible.sync="visible.value">
複製程式碼

當子元件改變值時改變的是 visible 物件中的 value 屬性。這樣就可以通過子元件直接改變父元件的值了。

18. 九宮格的實現

Vue 開發經驗小記(持續更新)

實現類似的九宮格程式碼:

<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>

複製程式碼

重點:

  1. 想要自動換行,通過以下三行程式碼實現:

    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    複製程式碼
  2. 每行幾個圖示,通過 item 的寬度控制。如果每行四個圖示,那 item 的寬度就是 25%,如果每行5個圖示,那每個 item 的寬度就是 20%.

19. 超出寬度橫向滑動

當子元件的寬度超過父元件,實現橫向滑動。
父元件可以是整個螢幕的根元素,也可以是某個特定的元素。只要設定好 css 即可。
設父元素的 class=parent,子元素的 class=child

.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: 必須結合的屬性,設定或檢索伸縮盒物件的子元素的排列方式。

overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
複製程式碼

21. flex 佈局中,單個元素靠右對齊

如下圖,姓名、性別、評論圖示這一行有三個元素,是 flex 佈局,前兩個元素靠左,評論圖示靠右。

Vue 開發經驗小記(持續更新)

已知父元素的佈局為

display: flex;
flex-direction: row;
align-items: center;
複製程式碼

實現起來有三種方法:

  1. 給姓名和性別兩個元素再加一層 div, 並把這個 div 設定 flex: 1 。缺點是多了層巢狀,有點麻煩。
  2. 給評論圖示這個元素設定
    flex: 1;
    text-align: right;
    複製程式碼
  3. 給評論圖示這個元素設定
    margin-left: auto;
    複製程式碼

後兩種方法都比較簡單,推薦。

相關文章