建立一個完整的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檔案,新增路徑程式碼