1. 程式人生 > 實用技巧 >建立一個完整的vue移動端專案配置

建立一個完整的vue移動端專案配置

vue的安裝及建立一個新專案前一篇文章已經介紹過了,有需要的請看之前的文章

移動端專案

一、關於移動端的一些ui框架
1.Voinc 一個基於 vue.js 和 ionic 樣式的 UI 框架(https://wangdahoo.github.io/vonic-documents/#/?id=%E4%BB%8B%E7%BB%8D)
2.Vux 基於WeUI和Vue(2.x)開發的移動端UI元件庫 (https://vux.li/#/?id=%E7%AE%80%E4%BB%8B)
3. NutUI 京東輕量級移動端Vue元件庫(https://nutui.jd.com/#/index)
4. .Mint UI 由餓了麼前端團隊推出的 Mint UI (http://mint-ui.github.io/docs/#/zh-cn2)
5. Vant是有贊前端團隊基於有贊統一的規範實現的 Vue 元件庫(https://github.com/youzan/zent)
6. Cube UI 滴滴 WebApp實現的移動端元件庫(https://didi.github.io/cube-ui/#/zh-CN/docs/quick-start)

二、移動端適配

方案:lib-flexible會自動在html的head中新增一個meta name="viewport"的標籤,同時會自動設定html的font-size為螢幕寬度除以10,也就是1rem等於html根節點的font-size。使用postcss-px2rem-exclude自動將css中的px轉成rem

步驟:1.專案中引入lib-flexible npm install lib-flexible --save
2.在專案的入口main.js檔案中引入lib-flexible import ‘lib-flexible/flexible.js’
3.安裝postcss-px2rem-exclude npm install postcss-px2rem-exclude --save
4.在專案的根目錄下找到檔案.postcssrc.js,在裡面新增如下程式碼

"postcss-px2rem-exclude": { 
      remUnit: 75,
      exclude: /node_modules|folder_name/i // 忽略node_modules目錄下的檔案(引入的第三方ui的樣式不會隨之改變)
    }

  • 1
  • 2
  • 3
  • 4
  • 5

5.在依賴檔案node_modules中找到postcss-px2rem-exclude檔案中的lib下的index.js新增以下程式碼(引入的第三方ui的樣式不會隨之改變)

try {
      var flag = options.exclude.includes('/')
      if (flag) {
        var arr = options.exclude.split('/')
        options.exclude = new RegExp(arr[1], arr[2])
      }
    } catch (error) {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

二.一 安裝sass-loader(如果使用到了sass編譯再安裝,不使用無需安裝)

npm install --save-dev sass-loader  
//sass-loader依賴於node-sass  ,所以繼續安裝node-sass
npm install --save-dev node-sass  
//注意1.安裝node-sass會報錯的原因,一般是因為網慢,所以安裝node-sass的時候可以使用cnpm安裝
//注意2.sass-loader安裝了但是編譯還是報錯,原因可能是sass-loader版本的問題(8.0.0),在package.json中把sass-loader的版本改成7.3.1,然後刪除依賴項,重新安裝依賴
  • 1
  • 2
  • 3
  • 4
  • 5

三、給IDE裝vue外掛(我現在使用的是vscode,就以vscode為例)
1.在vscode擴充套件裡搜尋Vetur

2.安裝完成進行配置
檔案–>首選項–>使用者程式碼片段–>點選新建程式碼片段–取名vue.json 確定
3.刪除多餘程式碼
4.貼上下邊的程式碼vue模板 (如果使用css預編譯,則style的型別是scss,如不使用改為css)

{
    "Print to console": {
        "prefix": "vue",   
        "body": [
            "<!-- $1 -->",
            "<template>",
            "<div class='$2'>$5</div>",
            "</template>",
            "",
            "<script>",
            "//這裡可以匯入其他檔案(比如:元件,工具js,第三方外掛js,json檔案,圖片檔案等等)",
            "//例如:import 《元件名稱》 from '《元件路徑》';",
            "",
            "export default {",
            "//import引入的元件需要注入到物件中才能使用",
            "components: {},",
            "data() {",
            "//這裡存放資料",
            "return {",
            "",
            "};",
            "},",
            "//監聽屬性 類似於data概念",
            "computed: {},",
            "//監控data中的資料變化",
            "watch: {},",
            "//方法集合",
            "methods: {",
            "",
            "},",
            "//生命週期 - 建立完成(可以訪問當前this例項)",
            "created() {",
            "",
            "},",
            "//生命週期 - 掛載完成(可以訪問DOM元素)",
            "mounted() {",
            "",
            "},",
            "beforeCreate() {}, //生命週期 - 建立之前",
            "beforeMount() {}, //生命週期 - 掛載之前",
            "beforeUpdate() {}, //生命週期 - 更新之前",
            "updated() {}, //生命週期 - 更新之後",
            "beforeDestroy() {}, //生命週期 - 銷燬之前",
            "destroyed() {}, //生命週期 - 銷燬完成",
            "activated() {}, //如果頁面有keep-alive快取功能,這個函式會觸發",
            "}",
            "</script>",
            "<style lang='scss' scoped>",
            "//@import url($3); 引入公共css類",
            "$4",
            "</style>"
        ],
        "description": "Log output to console"
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

6.上面程式碼中的 “prefix”: “vue”, 就是快捷鍵;儲存好之後新建.vue結尾的檔案試試(輸入vue 按tab鍵就可以)


四、vue路由
1.定義元件,剛剛已經用模板定義了一個元件
2.在router index.js檔案裡引入剛剛寫好的元件

我在這裡是用的vue-router提供的按模組載入方式
下面2行程式碼,沒有指定webpackChunkName,每個元件打包成一個js檔案。

const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')
const ImportFuncDemo2 = () => import('../components/ImportFuncDemo2')
  • 1
  • 2

下面2行程式碼,指定了相同的webpackChunkName,會合並打包成一個js檔案。

// const ImportFuncDemo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo')
// const ImportFuncDemo2 = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo2')
  • 1
  • 2

3.配置


import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter)
// 按模組載入...
const HelloWorld= () => import('../components/HelloWorld')


 const routes=[{
    path: '/',
    redirect: '/HelloWorld',
    name:'HelloWorld'
  },{
    path:'/HelloWorld',
    component: HelloWorld,
    name:'HelloWorld'
  }
]

const router = new VueRouter({
	 routes,
   mode: 'history',
  //  vuex的嚴格模式
   strict: process.env.NODE_ENV !== 'production',
  // 這個整體做的是:在路由的history模式下,一些列表頁利用快取模式來記錄位置(一般是返回不重新整理,前進重新整理),一般用了scrollBehavior,
  //同時還用keep-alive(快取),activated(快取下觸發的鉤子)配合做列表頁的返回記錄位置。快取模式也有坑,就是何時清除快取,一般是從新進入頁面就清除。
  //回到主題,滾動行為就是:例如一個列表頁,滑動了很多,點進去、再返回記錄剛剛的位置
	scrollBehavior (to, from, savedPosition) {
	    if (savedPosition) {
		    return savedPosition
		} else {
			if (from.meta.keepAlive) {
				from.meta.savedPosition = document.body.scrollTop;
			}
		    return { x: 0, y: to.meta.savedPosition ||0}
		}
	}
})

export default router

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

4.路由檔案注入到main.js檔案中

import Vue from 'vue';
import router from './router/index';
import 'lib-flexible/flexible.js';
import App from './App'


new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

5.在 app.vue裡配置router-view

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5

6.路由跳轉及傳參方式

 1.$router為VueRouter例項,想要導航到不同URL,則使用$router.push方法
 2.$route為當前router跳轉物件,裡面可以獲取name、path、query、params等


(1)設定動態路由
        {
    		path:'/shopDetails/:id',
   	 		component: shopDetails,
   			 name:'shopDetails'
         }
        跳轉this.$router.push('/shopDetails/'+id)
        獲取id通過   this.$route.params.id
       
(2)通過params攜帶引數 ,路由屬性中的name來匹配路由    
        
         {
    	 	path:'/shopDetails',
		   	 component: shopDetails,
		   	 name:'shopDetails'
         }
         跳轉 this.$router.push({name:'shopDetails',params:{id:id}})  
         獲取  this.$route.params.id
(3)通過path匹配路由,query攜帶引數 這種情況下 query傳遞的引數會顯示在url後面?id=?
         {
	    	 path:'/shopDetails',
		   	 component: shopDetails,
		   	 name:'shopDetails'
         }
         跳轉 this.$router.push({path:'/shopDetails',query:{id:id}})  
         獲取  this.$route.query.id
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

7.query和params的區別

1.動態路由和query屬性傳值 頁面重新整理引數不會丟失, params會丟失 2.動態路由一般用來傳一個引數時居多(如詳情頁的id), query、params可以傳遞一個也可以傳遞多個引數 。
2.直白的來說query相當於get請求,頁面跳轉的時候,可以在位址列看到請求引數,而params相當於post請求,引數不會再位址列中顯示

8.路由的模式

五、axios請求

axios文件上的教程在這裡就不重複敘述了,需要的看文件,我在這裡貼下我的使用教程
axios文件地址:https://www.npmjs.com/package/axios

1.安裝 npm install axios
2.在utils下新建一個axios檔案用來配置axios

import axios from 'axios';
const qs = require('qs');
const service=axios.create({
    baseURL: process.env.BASE_API,    //請求公共地址,baseURL`將被新增到`url`,所以後邊請求只需api的方式即可
    timeout: 5000,    //請求響應時間
})
// 是否攜帶cookie資訊,預設為false,根據專案需求設定
service.defaults.withCredentials = true;
// 新增一個請求攔截器
service.interceptors.request.use(function (config) {
    // 對請求資料做些事
    if(config.method  === 'post'){  //post傳參序列化
      config.data = qs.stringify(config.data);
    } 
    return config;
  }, function (error) {
    return Promise.reject(error);
  });

// 新增一個響應攔截器
service.interceptors.response.use(function (response) {
    // 對響應資料做些事
    return response;
  }, function (error) {
      console.log(error)
    // Do something with response error
    return Promise.reject(error);
  });
export default  service;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

3.新建一個api檔案,這裡邊存放所有的請求函式,方便查詢管理

import axios from '../utils/axios';
// 首頁banner
export function banner(){
    return axios({
        url:'/wxchat/xxx.action', //請求的地址
        method:'POST'
    })
}
// post請求帶引數
export function qiang(activityStatusType){
    return axios({
        url:'/wxchat/xxx.action',
        method:'POST',
        data:{activityStatusType:activityStatusType}
    })
}
// get請求
export function moneys(){
	 return axios({
        url: '/sd-web/xxx',
        method: 'get'
    }); 
}
// get請求帶引數
export function moneys(ids){
	 return axios({
        url: '/sd-web/xxx',
        method: 'get',
        params:{id:ids}
    }); 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

4.頁面實際應用

 import {banner} from '../../api/api'; //在頁面上引入需要的請求函式
  //在生命週期函式或者需要的方法裡運用
    mounted() {
        banner().then(response => {
            this.banner=response.data.data.SlAdvertisTabList
        }).catch(err=>{
             console.log(err)
        });
    },
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5.跨域配置

(1)在config資料夾寫的index.js裡配置跨域

proxyTable: {
    '/wxchat': {
      target: 'xxx',  //目標介面域名
      changeOrigin: true,  //是否跨域
      secure: false,  //target預設情況下,不接受執行在HTTPS上,且使用了無效證書的後端伺服器。如果你想要接受, 則需設定該項為false
      // pathRewrite: { // 如果介面本身沒有/wxchat需要通過pathRewrite來重寫了地址      重寫介面
      //   '^/wxchat: ' '   
      // }
    }
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注:我寫的這個專案裡,本身存在/wechat通用字首,所以我沒有用pathRewrite重寫,如果你們接口裡沒有通用字首的話,是要進行重寫的

上面配置中,’^/wxchat’ 其實是一個正則表示式

‘^/wxchat’ 應該拆分成 ‘^’ 和 ‘/wxchat’ 兩個字串,其中 ‘^’ 匹配的是字串最開始的位置。

‘/wxchat’: {}, 就是告訴node, 我介面只要是’/wxchat’開頭的才用代理.所以你的介面就要這麼寫/wxchat/xx/xx. 最後代理的路徑就是 http://xxx.xx.com/wxchat/xx/xx.
可是不對啊, 我正確的介面路徑裡面沒有/wxchat啊. 所以就需要 pathRewrite,用’’^/wxchat’’:’’, 把’/wxchat’去掉, 這樣既能有正確標識, 又能在請求介面的時候去掉wxchat

介面沒有通用字首的開發環境的配置

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  BASE_API: '"/wechat"',     //在跨域裡配置的//wechat,接口裡就不用寫通用字首了,就按照正常介面寫就可以了
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

介面有通用字首的開發環境配置

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  BASE_API: '""',     //這裡為空就好了,通用字首就寫在接口裡
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

六、vuex的使用

關於vuex的使用 ,什麼時候該使用vuex? 當你覺得需要全域性的狀態,但是通過別的方式又太麻煩的時候,你就需要了,比如說我現在做的一個app專案,header元件是一個公共元件,那麼header元件裡邊的標題怎麼能隨著頁面的跳轉而改變呢,vuex就可以做到 (個人理解,有不同理解的可以評論)
缺點:vuex頁面重新重新整理,資料會丟失 解決方法:存本地,state資料可以從本地獲取

1.vuex中,有預設的五種基本的物件:

  • state 全域性要訪問的值
  • getters 實時監聽state值的變化,對資料獲取之前的再次編譯,可以理解為state的計算屬性。
  • mutations 改變state裡的初始值 同步的
  • actions 非同步觸發mutations裡面的方法
  • modules:store的子模組,為了開發大型專案,方便狀態管理而使用的。這裡我們就不解釋了,用起來和上面的一樣。

2.下載安裝vuex npm install vuex --save
3.在src下新建一個store資料夾建立一個index.js 用來配置.
4.引入vue,vuex,使用use全域性注入外掛

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store=new Vuex.Store({
    // 設定全域性要訪問的state值
    state:{
       title:'測試標題',    //頭部標題
    }
})
export default store;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5.在main.js裡引入store,並全域性注入

import Vue from 'vue';
import router from './router/index';
import store from './store';
import 'lib-flexible/flexible.js';
import App from './App'


new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

6.在任意元件內測試剛剛寫的標題

<template>
    <div class='orderDetails'>
        {{$store.state.title}}
    </div>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5

7.如圖所示,剛剛的標題已經設定成功了

8.上圖所示已經成功設定了標題,那怎麼讓它的標題改變呢,就要用到mutations物件了,我們在mutations物件裡定義一個改變標題的方法,mutations裡面的引數,第一個預設為state,接下來的為自定義引數。

 // 改變state裡的初始值 同步的
    mutations :{
        TITLE(state,title){
            return state.title=title
        },
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

9.看下測試結

9.從圖上可以看出這個已經實現了,接下來看非同步操作

 // 非同步觸發mutations裡面的方法 在外部元件裡進行全域性執行actions裡面方法的時候,你只需要用執行this.$store.dispatch('title',132) 這樣就可以全域性改變改變標題的值了
    actions:{
       title({commit},title){
            commit('TITLE',title)
        },
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

就不上圖了,跟之前同步操作是一樣的結果

10.vuex同步和非同步的區別

1、當點發送過快,頁面中渲染的內容與state中的資料不一致,vuex裡面的state變得慢,且不持續更新
2、action中是可以做到頁面中state中資料保持一致
3、當你的操作行為中含有非同步操作,比如向後臺傳送請求獲取資料,就需要使用action的dispatch去完成了。其他使用commit即可。

七、打包上線
1.

執行  npm run build 命令
  • 1

2.執行完成後,你會發現你的目錄下多了一個dist的資料夾,這個就是打包的資料

3.打包之後出現頁面空白的原因
3.1 css,js路徑引用錯誤的問題

解決:到config資料夾中開啟index.js檔案。
檔案裡面有兩個assetsPublicPath屬性,更改第一個,也就是更改build裡面的assetsPublicPath屬性:
assetsPublicPath屬性作用是指定編譯釋出的根目錄,‘/’指的是專案的根目錄 ,’./’指的是當前目錄。

3.2 設定路由history模式

解決:改為hash或者直接把模式配置刪除,讓它預設的就行 。如果非要使用history模式的話,需要你在服務端加一個覆蓋所有的情況的候選資源:如果URL匹配不到任何靜態資源,則應該返回一個index.html,這個頁面就是你app依賴頁面。

3.3 在css中引入的背景圖片的路徑問題

解決:到build資料夾中開啟util.js檔案,新增路徑程式碼