1. 程式人生 > 其它 >Vue3.0入門 + Vant3.0移動端實踐(二)輪播圖模組封裝及首頁完善

Vue3.0入門 + Vant3.0移動端實踐(二)輪播圖模組封裝及首頁完善

技術標籤:vue.js

接著上一篇的來,上一篇介紹了環境搭建及做好了底部的導航欄模組,接下來繼續完善首頁。

先來張最終效果圖:

記錄下之前遇到的問題,Vue中img影象src變成"[object Module]"無法正確載入的問題。

我在vue專案的js程式碼中,使用了"imgUrl"=require('../asserts/image.png')這種形式。網上查了很多資料,說是因為file-loader預設採用ES模組語法,即import '../image.png';然而Vue生成的是CommonJS模組語法,即require('../image.png')

;二者不一致。要麼讓file-loader或url-loader採用CommonJS語法,要麼讓Vue採用ES語法。

但是我找了下專案中的各個檔案,沒找到在哪能改esModule: false的選項,於是暫時作罷。

最終我找到的折中的辦法,本地圖片和css資源都放在public目錄下,這樣就可以了。

本地的css檔案在index.html檔案里加載,如:

<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="<%= BASE_URL %>css/font_nbicon.css">

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
	<link rel="stylesheet" href="<%= BASE_URL %>css/font_nbicon.css">
    <title>物聯網報警系統</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

以下是輪播圖模組封裝,本地的圖片放在public目錄裡,使用時直接引入路徑即可:

在components目錄下新建Swiper.vue元件:

<template>
  <van-swipe class="my-swipe" :autoplay="3000" indicator-color="#1baeae">
    <van-swipe-item v-for="(item, index) in list" :key="index">
      <img :src="item.imgUrl" alt="暫無圖片">
    </van-swipe-item>
  </van-swipe>
</template>

<script>
export default {
  props: {
    list: Array
  },
  methods: {
   
  }
}
</script>

<style lang='less' scoped>
  .my-swipe {
	  display: flex;
	  flex-shrink: 0;
	  flex-wrap: wrap;
	  width: 100%;
	  padding-top: 45px;
    img {
      width: 100%;
      height: 100%;
    }
  }
</style>

style調整介面的less寫法,

display: flex;含義為使用flex佈局。

padding-top: 45px;為輪播圖最上面的titile留出45px的位置。

js的props屬性裡增加了個list,型別為Array,作為子元件接收屬性。

接下來看看首頁Home.vue裡如何用:

import swiper from '@/components/Swiper'

先import引入進來,export default的components裡註冊元件。

template模板標籤裡這麼使用:<swiper :list="swiperList"></swiper>

注意setup(),ref,torefs,reactive都為vue3的新用法,setup是Vue3 的一大特性函式 。

setup函式是處於 生命週期函式 beforeCreate 和 Created 兩個鉤子函式之間的函式。

setup函式是 Composition API(組合API)的入口。

在setup函式中定義的變數和方法最後都是需要 return 出去的 不然無法再模板中使用。

在執行 setup函式的時候,還沒有執行 Created 生命週期方法,所以在 setup 函式中,無法使用 data 和 methods 的變數和方法。

setup函式只能是同步的不能是非同步的。

舊的Vue2.0 Options API 和 新的Vue3.0 Composition API 區別

Vue2.0 Options API 約定:

我們需要在 props 裡面設定接收引數

我們需要在 data 裡面設定變數

我們需要在 computed 裡面設定計算屬性

我們需要在 watch 裡面設定監聽屬性

我們需要在 methods 裡面設定事件方法

你會發現 Options APi 都約定了我們該在哪個位置做什麼事,這反倒在一定程度上也強制我們進行了程式碼分割。

現在用 Composition API,不再這麼約定了,於是乎,程式碼組織非常靈活,我們的控制程式碼寫在 setup 裡面即可。

setup函式提供了兩個引數 props和context,重要的是在setup函式裡沒有了this,在vue3.0中,訪問他們變成以下形式: this.xxx=》context.xxx

我們沒有了 this 上下文,沒有了 Options API 的強制程式碼分離,Composition API 給了我們更加廣闊的天地。

接下來說下ref和reactive。vue3在9月18號晚上釋出了,在vue3中對響應式資料的宣告官方給出了ref()和reactive()這兩種方式。

雙向資料繫結,vue一共提供了兩種資料響應式監聽,有點React Hooks的味道。ref 函式傳入一個值作為引數,返回一個基於該值的響應式Ref物件,該物件中的值一旦被改變和訪問,都會被跟蹤到,就像我們改寫後的示例程式碼一樣,通過修改 count.value 的值,可以觸發模板的重新渲染,顯示最新的值。
其實,除了 ref 函式,Vue3.0中還提供了另外一個可以建立響應式物件的函式,那就是 reactive 函式。下面就來說說為什麼要提供兩種API

ref寫法簡單,但也有弊端,經過嘗試發現他只能監聽一些如數字、字串、布林之類的簡單資料。

ref修改資料需要使用這樣count.value=xxx的形式,而reactive只需要state.reactiveField=值這樣來使用。

reactive在return時候需要toRefs來轉換成響應式物件。

toRefs函式能將reactive建立的響應式物件,轉化成為普通的物件,並且這個物件上的每個節點,都是ref()型別的響應式資料。

在Vue中使用可以直接使用ref (template中),Vue會自動新增.value。在Js中使用ref需要.value。

setup函式必須有返回值,必須返回個物件。

setup函式有一個props引數,用於接收props,也就是定義在元件上的屬性(同vue2),但是接收的props必須先在props屬性中定義,否則是不會被接收到。

vue2的所有生命週期寫法與vue3相容,而在vue3中,生命週期添加了on字首,需要匯入並寫在setup()函式中。

<template>
	<div>
		<header class="home-header wrap" :class="{'active' : headerScroll}">
		  <div class="header-search">
		    <span class="app-name">物聯網報警系統</span>
		  </div>
		</header>
		 <nav-bar />
		 <swiper :list="swiperList"></swiper>
		 <div class="category-list">
		   <div v-for="item in categoryList" v-bind:key="item.categoryId" @click="tips">
		     <img :src="item.imgUrl">
		     <span>{{item.name}}</span>
		   </div>
		 </div>
	</div>
</template>

<script>
import navBar from '@/components/NavBar'
import swiper from '@/components/Swiper'
import { getHome } from '@/service/home'
import { Toast } from 'vant'
import { reactive, onMounted , toRefs} from 'vue'
export default {
	name: 'home',
	components: {
	  navBar,
	  swiper
	},
	setup() {
		const state = reactive({
		  swiperList: [] ,// 輪播圖列表
		  categoryList: [
		   {
		      name: '檢視裝置',
		      imgUrl: '/image/kj1.png',
		      categoryId: 100001
		    }, {
		      name: '使用者資訊',
		      imgUrl: '/image/kj2.png',
		      categoryId: 100002
		    }, {
		      name: '充值中心',
		      imgUrl: '/image/kj3.png',
		      categoryId: 100003
		    },{
		      name: '功能預留',
		      imgUrl: '/image/kj4.png',
		      categoryId: 100004
		    }, {
		      name: '全部',
		      imgUrl: '/image/kj5.png',
		      categoryId: 100005
		    }
		  ],
		  })
		  
		onMounted(async () => {
			Toast.loading({
			  message: '載入中...',
			  forbidClick: true
			});
			const { data } = await getHome()
			console.log(data)
			state.swiperList = data.swiperLists
			Toast.clear()
		})
		
		const tips = () => {
		  Toast('敬請期待');
		}
		
		return {
		  ...toRefs(state),
		   tips
		}
	}
}
</script>

<style lang="less" scoped >
  @import '../common/style/mixin';
 .home-header {
	 position: fixed;
	 left: 0;
	 top: 0;
	 .wh(100%, 45px);
	 .fj(center);
	 line-height: 45px;
	 padding: 0 15px;
	 .boxSizing();
	 font-size: 15px;
	 color: #fff;
	 background: #39A2FD;
	 z-index: 10000;
	 
	 .app-name {
		 padding: 0 5px;
		 color: #ffffff;
		 font-size: 18px;
	}
  }
  .category-list {
    display: flex;
    flex-shrink: 0;
    flex-wrap: wrap;
    width: 100%;
    padding-bottom: 13px;
    div {
      display: flex;
      flex-direction: column;
      width: 20%;
      text-align: center;
      img {
        .wh(36px, 36px);
        margin: 13px auto 8px auto;
      }
    }
  }
</style>

至此,首頁基本設計完成,接下來會繼續介紹個人中心頁面的詳細設計過程。先來張截圖吧: