?? 個人主頁歡迎訪問 ??
最近在開發(fā)一個移動端項目微信公眾號,前端框架我用當(dāng)然是Vue,期間遇到了很多問題,在這里記錄一下,希望也可以幫助到入坑vue項目的伙伴們,先說說我用到的技術(shù)棧:
vue:前端主體框架vue 2.0
vue-router:vue全家桶之一 vue的路由
axios:基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中配合vue-axios完成vue中和后臺對接url發(fā)送請求
vuex:vue全家桶之一 vue的狀態(tài)管理器
Webpack:webpack 3.0在nodejs下用于對項目的模塊化管理方案,或者是自動化打包工具,將我們項目中的所有文件經(jīng)過loader之后解析為瀏覽器識別的js模塊
vux:UI框架
Less:less語言 打包后自動編譯為css
ESNext:es寫法 完美支持vue 2.0
Rem:整個項目采用rem布局來兼容各個尺寸的手機(jī)
Jquery:jquery庫
Flex:flex布局更方便快捷
vue-quill-editor:基于quill插件開發(fā)的一款支持vue的富文本編輯器
vue-scroller:滾動條 上拉加載
vue-cropper:截圖工具
mint-ui:IndexList首字母索引列表
為了趕項目進(jìn)度這里直接安裝的vue-cli腳手架,不會搭建vue項目的可以看我的兩外兩個文章:
Vue 2.x + Webpack 4.x的那些事---萌新必備
Vue-cli+Element+Less開發(fā)
接下來把在項目開發(fā)的過程中遇到的幾個坑和幾個vue開發(fā)小技巧分享給大家:
一、 rem布局
所謂rem布局就是一種彈性布局--等比縮放,就是開發(fā)之前,先考慮好移動端的適配問題,因為移動端會出現(xiàn)各種尺寸,不能尺寸大小都一樣
//假設(shè)一個比例,然后根據(jù)這個比例和我們的UI設(shè)計圖計算出我們想要的適配方案
// 首先在css全局里面寫入:
html{
font-size: 20px;
}
// js全局寫入:
var rem = 20; // 設(shè) 全局的html的font-size默認(rèn)是20px
;(function(){
// 320: 設(shè)備大小 原比例1:1 寬度320px
// 假設(shè)1px = (20/320)rem 即1rem = 20px
// 實際根據(jù)UI設(shè)計圖 如果設(shè)計圖大小寬度為640 那么640/320=2 所以設(shè)計圖是2倍圖
// 綜上所述 2倍圖應(yīng)該是20*2=40px 即實際1rem = 40px
// 所以在設(shè)計圖量的尺寸(像素)/40就是實際要在頁面寫入的rem exp: 測量設(shè)計圖中的某一div寬度為80px 經(jīng)過計算80/40=2 那么寫入樣式就應(yīng)該是width: 2rem;
rem = 20/320*document.documentElement.clientWidth; // 20/320 = 實際字體大小/實際設(shè)備寬度
document.documentElement.style.fontSize = rem+'px';
window.onresize = function(){
rem = 20/320*document.documentElement.clientWidth;
document.documentElement.style.fontSize = rem+'px';
};
})();
二、 代理
在和后臺做交互數(shù)據(jù)的過程中,避免不了出現(xiàn)不同端口或者不同域名,這個時候我們就要做代理,首先vue提倡的交互工具是axios這里就不多說了,至于為什么技術(shù)棧里會涉及到vue-axios,是為了更好地適配vue而做的
// 首先在全局引入:
import axios from 'axios';
import VueAxios from 'vue-axios';
Vue.use(VueAxios,axios);
在config文件夾下有一個index.js,該文件就是webpack的配置,里面有一個proxyTable對象,將下面的代碼放進(jìn)去:
'/api': { //將映射為/api
target: 'http://192.168.0.242:8080/', // 后臺的接口域名
secure: false, // 如果是https接口,需要配置這個參數(shù)
changeOrigin: true, //是否跨域
pathRewrite: { // 如果接口本身沒有/api需要通過pathRewrite來重寫地址
'^/api': ''
}
}
在vue中這樣交互:
// 在代碼里通過axios和后臺進(jìn)行交互:
var url = '/api/wechatRegister/sendCodeToMoile.htm' // 原地址為: http://192.168.0.242:8080/wechatRegister/sendCodeToMoile.htm
var paramsObj = {
mobile: this.phoneNumber
}
this.axios.get(url, {
params: paramsObj
}).then((res) => {
// coding...
}).catch((response) => {
console.log(response)
})
這樣就可以完成代理進(jìn)行交互了,但是在你之后打包項目到生產(chǎn)環(huán)境的時候你會發(fā)現(xiàn)并無法訪問;這里有幾個坑需要填一下:
三、 webpack配置問題(打包之后可能會出現(xiàn)的問題)
之前項目遇到過的一些問題:
配置vue+webpack踩過的坑
Vue中使用錨點定位跳轉(zhuǎn)失效
vue中使用監(jiān)聽器(watch)需要注意的問題
vue項目樣式中的scoped屬性
vue+webpack 綁定src找不到圖片報404
以下是本次項目中遇到的一些問題:
(1)路徑問題
- 如果項目中采用了反向代理Nginx的話,那么config/index.js配置中的build下的
assetsPublicPath一定要用./(/代表域名的根目錄,./代表當(dāng)前路徑) - 如果打包之后背景圖片讀取不到,就在
build/utils.js下添加一句代碼publicPath: '../../',像這樣:
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader',
publicPath: '../../'
})
(2) 代理的交互接口請求不到
發(fā)現(xiàn)所有請求都失效報錯了,在network下查看地址都錯了,這是因為開發(fā)環(huán)境和生產(chǎn)環(huán)境不一樣,地址需要根據(jù)環(huán)境變化的,所以我的解決方案是定義一個全局變量掛載在vue的實例上,該全局變量通過識別生產(chǎn)或是開發(fā)模式來給webpack的代理一個值,再將該變量寫入每一個接口中:
- plugins/global.js 自定義的一個全局js文件:
//項目域名地址
const url = 'http://192.168.0.22:8083';
let ROOT;
//由于封裝的axios請求中,會將ROOT打包進(jìn)去,為了方便之后不再更改,判斷了當(dāng)前環(huán)境,而生成的不同的ROOT
if (process.env.NODE_ENV == 'development') {
//開發(fā)環(huán)境下的代理地址,解決本地跨域跨域,配置在config目錄下的index.js dev.proxyTable中
ROOT = "/api"
} else {
//生產(chǎn)環(huán)境下的地址
ROOT = url;
}
exports.PROXYROOT = url; //代理指向地址
exports.ROOT = ROOT;
- config/index.js webpack配置文件:
const config = require('../src/plugins/global');
proxyTable: {
[config.ROOT]: {
target: config.PROXYROOT,
secure: false,
changeOrigin: true,
pathRewrite: {
['^'+config.ROOT]: ''
}
}
}
- main.js 入口文件:
const variable = require('@/plugins/global');
Vue.prototype.api_config = variable.PROXYROOT;
- vue交互代碼:
var url = this.api_config + '/wechatRegister/sendCodeToMoile.htm'
var paramsObj = {
mobile: this.phoneNumber
}
this.axios.get(url, {
params: paramsObj
}).then((res) => {
// coding...
}).catch((response) => {
console.log(response)
})
(3)項目中的icon處理
打包之后的一些小icon圖標(biāo)vue都會以base64打包掉,而不會打包在assets下以圖片形式展示的
(4)慎用eslint規(guī)范
如果沒有那么了解eslint編碼規(guī)范的話,就把build下的webpack.base.conf.js中的這塊代碼注釋掉,像這樣:
const createLintingRule = () => ({
// test: /\.(js|vue)$/,
// loader: 'eslint-loader',
// enforce: 'pre',
// include: [resolve('src'), resolve('test')],
// options: {
// formatter: require('eslint-friendly-formatter'),
// emitWarning: !config.dev.showEslintErrorsInOverlay
// }
})
用了這個的話,一些編碼不規(guī)范,vue就會報錯
(5)npm包管理
在項目中安裝一些npm依賴包的時候,會被分成兩組對象dependencies和devDependencies;
-
dependencies是生產(chǎn)環(huán)境需要的包,通過npm install -S命令進(jìn)行安裝 -
devDependencies是開發(fā)環(huán)境需要的包,通過npm install -D命令進(jìn)行安裝,生產(chǎn)環(huán)境就不需要了
所以在我們代碼里面使用的一些功能性插件和UI組件一定要安裝在dependencies中,否則在生產(chǎn)環(huán)境功能就會失效
(6)jquery或$ is not defined
如果想在vue中引入jquery,那么它的正確使用方式是這樣的:
- 安裝jquery:
npm i jquery -S
- 在webpack里面配置一下 build/webpack.base.conf.js:
// 在文件最頂端如果沒引入webpack的話引入一下
const webpack = require("webpack")
plugins: [
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery"
})
],
然后無需引入,在vue代碼中直接使用即可 exp: $('#app')
(7)vux的webpack配置(這里特別注意不是vuex是vux UI框架)
使用了vux這個組件的朋友可以看一下,安裝引入之后,官方也說了,還需要在webpack配置一下,前面的步驟官方都寫的很明白,只是最后一步配置問題有很多伙伴不會,這里我也遇到了坑,所以記錄了一下:
在build/webpack.base.conf.js下:
// 引入vux的loader
const vuxLoader = require('vux-loader')
module.exports = vuxLoader.merge(webpackConfig, {
plugins: [
'vux-ui'
]
})
四、 小技巧
(1)雙擊事件實現(xiàn)方式
雙擊事件可以通過短時間內(nèi)的兩個單擊事件來實現(xiàn),就是給單擊事件加一個定時器,寫一個計算點擊的次數(shù),根據(jù)點擊的次數(shù)來達(dá)到雙擊的效果:
this.count ++; // 初始值設(shè)為0
let timer = setTimeout(() => {
if (this.count == 2) {
console.log('雙擊');
}
this.count = 0;
clearTimeout(timer);
}, 300);
(2)touchstart和click事件沖突
這兩個事件如果同時使用的時候,可能會引發(fā)沖突,即touchstart好使,click失效,這個問題來自于touchstart下阻止了默認(rèn)事件,導(dǎo)致click失效,需要去掉touchstart身上的取消默認(rèn)事件方法或?qū)傩?/p>
(3)微信公眾號更改文章標(biāo)題
在配置全局路由的時候,加一個meta對象,在meta對象里面寫一個title,該title就是我們微信公眾號文章上的title,在配置一個全局路由守衛(wèi),將路由中的title賦值給DOM的title:
import Vue from 'vue';
import Router from 'vue-router';
// 登錄
const Login = r => require.ensure([], () => r(require('@/components/login/Login.vue')), 'Login');
// 注冊
const Register = r => require.ensure([], () => r(require('@/components/register/Register.vue')), 'Register');
Vue.use(Router);
const router = new Router({
routes: [
{
path: '/',
redirect: '/login'
}, {
path: '/login',
name: 'Login',
component: Login,
meta: {
title: '用戶登錄'
}
}, {
path: '/register',
name: 'Register',
component: Register,
meta: {
title: '用戶注冊'
}
}
]
});
// 全局路由守衛(wèi)
router.beforeEach((to, from, next) => {
document.title = to.meta.title;
next();
});
export default router;
(4)微信自定義分享
- 安裝依賴包
npm install weixin-js-sdk
- 引入微信插件
import wx from 'weixin-js-sdk';
- 全局微信分享配置
我在全局引入了該方法,并且封裝在了vue下:
Vue.prototype.$wxConfig = function(appId, timestamp, nonceStr, signature) {
// 微信分享功能的配置項 全局引入
wx.config({
debug: false,
appId: appId, // 和獲取Ticke的必須一樣------必填,公眾號的唯一標(biāo)識
timestamp: timestamp, // 必填,生成簽名的時間戳
nonceStr: nonceStr, // 必填,生成簽名的隨機(jī)串
signature: signature, // 必填,簽名
// 需要分享的列表項:分享給朋友/QQ好友, 分享到朋友圈/QQ空間
jsApiList: [
'onMenuShareAppMessage',
'onMenuShareTimeline',
'onMenuShareQQ',
'onMenuShareQZone',
'updateAppMessageShareData',
'updateTimelineShareData'
]
});
}
在app.vue中使用:
通過axios請求拿到了這四個屬性值,然后調(diào)用傳參:
this.$wxConfig(this.appId, this.timestamp, this.nonceStr, this.signature);
再封裝一個可定義的分享插件:
Vue.prototype.$wechat = function(title, link, imgUrl, desc) {
// 微信分享功能分享之后的信息
// 處理驗證失敗的信息
wx.error(function (res) {
console.log('驗證失敗返回的信息:', res)
});
// 處理驗證成功的信息
wx.ready(function () {
var shareData = {
title: title, // 分享標(biāo)題
desc: desc, // 分享描述 這里請?zhí)貏e注意是要去除html
link: link, // 分享鏈接,該鏈接域名或路徑必須與當(dāng)前頁面對應(yīng)的公眾號JS安全域名一致window.location.href.split('#')[0]
imgUrl: imgUrl, // 分享圖標(biāo)
success: function (res) {
// 用戶確認(rèn)分享后執(zhí)行的回調(diào)函數(shù)
console.log('用戶確認(rèn)分享后執(zhí)行的回調(diào)函數(shù):', res)
},
cancel: function (res) {
// 用戶取消分享后執(zhí)行的回調(diào)函數(shù)
console.log('取消分享到朋友圈返回的信息為:', res)
}
};
if(wx.onMenuShareAppMessage){ //微信文檔中提到這兩個接口即將棄用,故判斷
wx.onMenuShareAppMessage(shareData); //1.0 分享到朋友
wx.onMenuShareTimeline(shareData); //1.0 分享到朋友圈
wx.onMenuShareQQ(shareData); //1.0 分享到QQ
wx.onMenuShareQZone(shareData); //1.0 分享到QQ空間
}else{
wx.updateAppMessageShareData(shareData); //1.4 分享給朋友/QQ好友
wx.updateTimelineShareData(shareData); //1.4 分享到朋友圈/QQ空間
}
})
}
然后再每個頁面初始化的時候調(diào)用上面的這個方法:
created () {
this.$wechat('公眾號文章標(biāo)題', 'http://www.baidu.com/', '圖片', '公眾號文章信息')
}
五、 兼容性問題
由于Android和IOS的系統(tǒng)以及瀏覽器內(nèi)核不同等,許多東西無法適配兼容,這里提供一個方案,可以判斷兩者將其分開來寫:
- 封裝好一個判斷設(shè)備的插件
Vue.prototype.$is_android = function() {
// 判斷當(dāng)前訪問的設(shè)備是安卓還是IOS
var userAgent = navigator.userAgent;
var isAndroid = userAgent.indexOf('Android') > -1 || userAgent.indexOf('Linux') > -1; // Android設(shè)備
var isIOS = !!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); // IOS設(shè)備
if (isAndroid) {
return true;
}
if (isIOS) {
return false;
}
}
- To use
let isAndroid = this.$is_android;
if (isAndroid) {
// Android設(shè)備訪問
// coding...
} else {
// IOS設(shè)備訪問
// coding...
}
雖然chrome瀏覽器自帶模擬機(jī)模擬移動端,但是畢竟是模擬機(jī),內(nèi)核什么的都是不同的,所以真實效果還是要在真機(jī)測試的,其中好多新的css3或h5的寫法在IOS下不兼容,測試出以下bug:
(1)css3背景
Android寫法(IOS下失效):
background: url('../images/icon/icon-right.png') center no-repeat / 100%;
IOS下需改成:
background: url('../images/icon/icon-right.png') center no-repeat;
background-size: 100%;
(2)flex布局
使用flex布局的時候需要注意一下
justify-content: space-evenly;在IOS下失效,要么使用space-around替代,要么換其他方式
(3)fixed屬性在IOS軟鍵盤彈出后失效
(4)安卓上點擊輸入框彈出軟鍵盤后蓋住一部分背景
首先全屏背景最外層盒子不可以設(shè)置overflow:hidden;,否則點擊頁面底部的輸入框彈出軟鍵盤之后會出現(xiàn)
被鍵盤蓋住,無法邊看邊輸入,頁面不能實時上下拖動,這是第一點;
第二點是本來整個頁面高度是100%的,結(jié)果彈出軟鍵盤之后,頁面的高度就變成了100%-軟鍵盤的高度,所以這個時候我們要事先一開始進(jìn)入項目就存一個可視區(qū)高到vuex里面,然后綁定一個行內(nèi)樣式style,再判斷之前存的可視區(qū)高和當(dāng)前的可視區(qū)高給頁面高度賦值來解決這個問題:
// 給最外層盒子綁定一個行內(nèi)高
:style="{ height: bodyHeight }"
// 掛載的時候進(jìn)行修改
mounted(){
if (document.documentElement.clientHeight < this.$store.state.clientHeight) {
this.bodyHeight = '100%';
} else {
this.bodyHeight = document.documentElement.clientHeight + 'px';
}
}
(5)Android和IOS上h5視頻的一些問題
由于在一部分路由的子路由里面使用video的話,IOS全屏播放再退出全屏的時候會出現(xiàn)閃退的情況,網(wǎng)上有這么一個屬性webkit-playsinline="true"和playsinline="true"設(shè)置上之后會禁止全屏播放,但事實并非如此,設(shè)置上之后一點效果也沒有,說是下面還需要給webview設(shè)置一個``,這個是在IOS官方API中提到的,讓我來說說為什么在視頻上會出現(xiàn)這么個問題:
問題出現(xiàn)的原因:
雖然都是移動端微信訪問視頻,但是安卓應(yīng)用中的采用的是QQ瀏覽器中播放視頻,而蘋果采用的是蘋果自帶的播放器
問題所涉及的內(nèi)容:
- 安卓上的微信播放視頻video:
點擊頁面上的視頻進(jìn)行播放,播放完了再回到頁面中,會出現(xiàn)視頻在文本最上層,頁面中的所有內(nèi)容不管是什么定位都會被蓋在視頻的下面,有一個屬性x5-video-orientation可以兼容android播放,讓video視頻不再是最上層,變成可控制的,但是會出現(xiàn)新的問題,點擊視頻之后可以聽到聲音但是沒有圖像顯示,一片黑,必須點擊橫向播放按鈕才可以正常播放,如果用了富文本編輯器插件問題會暴露的更多 - 蘋果上的微信播放視頻video:
正常怎么點擊播放都沒問題,但是在一部分路由的子路由里面使用video的話,IOS全屏播放再退出全屏的時候會出現(xiàn)閃退的情況
我的解決方案:
通過原生js對video進(jìn)行控制,一般業(yè)務(wù)都需要全屏,那我們就不要全屏,就對一些就簡單的功能入手,網(wǎng)上有原生video的一些事件方法,可以通過這些事件對video進(jìn)行控制,像這樣:
// result是從后臺獲取到的數(shù)據(jù) 里面包含iframe這樣的視頻
this.content = result.content.replace(/iframe/g, 'video');
this.$nextTick(()=>{
$('video').attr('controls', 'controls');
})
如果不是iframe的視頻,而是h5的video,就是下面這樣:
var aVideo = document.getElementsByTagName('video');
for (let i = 0; i < aVideo.length; i++) {
aVideo[i].onclick = function(){
if (this.paused) {
this.play();
} else {
this.pause();
}
};
}
(6)在IOS上彈出框顯示域名的問題
// alert() 和 confirm()
六、 項目前后的準(zhǔn)備
在開發(fā)項目前需要了解一下需求以及需要哪些插件,提前找好,當(dāng)然UI組件之類的可以提前找個差不多,但是插件的話找到了可能最后也會因為一些原因被淘汰掉,這里我先說一下如何選擇一款適合自己項目的UI;
vue的UI庫非常多,但是要找準(zhǔn)適合自己項目的:
- Cube UI:滴滴的基于 Vue.js 實現(xiàn)的精致移動端組件庫。 stars:6k
- Muse-UI:基于 Vue2.0 開發(fā)的組件庫。 stars:7k
- Element:餓了么前端開發(fā)的基于 Vue 2.0 的桌面端組件庫。 stars:38k
- Mint UI:餓了么前端開發(fā)的基于 Vue 2.0 的移動端組件庫。 stars:14k
- vue-element-admin:是一個后臺前端解決方案,它基于 vue 和 element-ui實現(xiàn)。 stars:35k
- uni-app:是一個使用 Vue.js 開發(fā)跨平臺應(yīng)用的前端框架。 stars:8k
- SUI Mobile:由阿里巴巴國際UED前端出品的移動端UI庫,是一套基于 Framework7 開發(fā)的UI庫,并且能兼容到 iOS 6.0+ 和 Android 4.0+,非常適合開發(fā)跨平臺Web App。 stars:6k
- WeUI:是一套同微信原生視覺體驗一致的基礎(chǔ)樣式庫,由微信官方設(shè)計團(tuán)隊為微信內(nèi)網(wǎng)頁和微信小程序量身設(shè)計,令用戶的使用感知更加統(tǒng)一。 stars:22k
- MUI:最接近原生APP體驗的高性能前端框架。 stars:11k
以上就是我所知道的所有vue的UI庫,以及他們是干什么的詳細(xì)介紹也展現(xiàn)給了大家,希望有幫助。
如果您想自己寫更多特效或者好看的字體庫,那么這里有兩個推薦:
- Animate.css:一個CSS動畫的跨瀏覽器庫。 stars:60k
- Awesome-vue:一套絕佳的圖標(biāo)字體庫和CSS框架。 stars:46k
項目中有富文本編輯器和截圖的需求,因此找了幾種插件:
富文本編輯器:
- Vue-Quill-Editor:基于 Quill、適用于 Vue 的富文本編輯器,這款可以用在移動端,是比較好用的一個。 stars:4k
- wangEditor:基于javascript和css開發(fā)的 Web富文本編輯器,只適用在PC端,移動端會出現(xiàn)很多問題。 stars:7.7k
- Vue2Editor:用于使用Vue.js和Quill.js構(gòu)建的富文本編輯,這款沒用過,個人感覺既然適配了vue,就應(yīng)該沒啥大問題。 stars:1.5k
關(guān)于Vue-Quill-Editor在vue中的詳細(xì)使用方式請戳這里;期間還用過兩款,不過讓我pass了,bug漏洞百出,問題很多;在使用前兩個編輯器的時候不會的可以問我,會的一定幫助大家
截圖工具:
- vue-cropper:該插件特別靈活使用特別簡單,還支持vue。
在開發(fā)過程中有很多問題需要總結(jié),會逐漸完善代碼,如果有什么問題的小伙伴們可以在這里給我留言哈,對大家有幫助的內(nèi)容可以提供下,一起學(xué)習(xí)共同進(jìn)步
如果喜歡本文的話單擊愛心加關(guān)注謝謝O(∩_∩)O~
歡迎訪問我的GitHub,喜歡的可以star,項目隨意fork,支持轉(zhuǎn)載但要下標(biāo)注