vue移动端项目经验
配置路径@(一般情况下搭建vue-cli,项目已经配置好了@路径。若自己需要改动可按照一下方法)
vue-cli 2.x 版本创建项目时,我们可以在 build 文件夹下找到 webpack.base.conf.js 文件,在里面修改 resolve.alias 即可
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
vue-cli 3.0 创建项目时,目录结构精简化,找不到 build 和 config 文件夹,那么该如何修改路径别名呢?
由于vue-cli 3.0 版本所有的配置项都放在了 vue.config.js 文件中,所以在里面进行配置就好了,代码如下:
const path = require('path');
function resolve (dir) {
return path.join(__dirname, dir)
}
// 项目的主要配置文件
module.exports = {
// webpack 配置进行更细粒度的修改 https://cli.vuejs.org/zh/config/#chainwebpack
chainWebpack: (config)=>{
//修改文件引入自定义路径
config.resolve.alias
.set('@', resolve('src'))
.set('style', resolve('src/assets/style'))
}
}
配置好想用的路径别名后,重新启动项目,再引入文件时,使用别名即可
import Demo from '@/demo/demoweb'
使用iconfont
1、在assets文件夹下新建iconfont文件夹
2、从iconfont下载字体图标包,下载下来之后,将文件中的这6个文件放入iconfont中(见下图)
3、在main.js中引入:import './assets/iconfont/iconfont.css'
(若有报错,安装css-loader包)
如何实现横向滚动(兼容safari,微信,浏览器)
实现横向滚动需要以下几点:
1、父级盒子要overflow-y:hidden;overflow-x:auto来防止页面宽度溢出,以及实现左右滚动效果
2、::-webkit-scrollbar { display: none;}隐藏横向滚动条(此方法在ios和微信端无效),所以第四步实现兼容
3、滚动条所存在的盒子为g,g之外再设置一个父盒子s并给定高度和overflow:hidden。通过s来遮盖g的滚动条即可实现隐藏滚动条。
示例:
<template>
<div class="university">
<div class="container">
<div class="scroll-hidden"> <!--此盒子设置:height,overflow:hidden-->
<div class="scroll-body"> <!--此盒子设置:滚动条overflow-y:hidden,overflow-x:auto,滚动条display:none,padding-bottom-->
<div class="scroll-secbody"> <!--此盒子设置 :10000px宽度-->
<div v-for="(item,index) in universityList" :key="index" class="every_content"> <!--此盒子设置: 浮动.。注:不要用flex-->
<div class="content_img"><img :src="item.pimage" /></div>
<div class="content_name">{{item.pname}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script></script>
<style scoped lang="less">
.university{
.container{
.scroll-hidden{
height:152px; //6、设置固定高度(以子盒子为准)
overflow:hidden; //7、超出部分隐藏
.scroll-body{
overflow-y: hidden; //2、父级盒子:竖向超出部分隐藏
overflow-x: auto; //3、父级盒子:控制横向可滚动,配合overflow-y:hidden共同控制内容不溢出当前屏幕
&::-webkit-scrollbar { //4、父级盒子:隐藏滚动条(不兼容ios和微信端)
display: none;
}
padding-bottom: 100px; //5、通过padding-bottom将滚动条挤到盒子外
.scroll-secbody{
white-space: nowrap; //1、强制不换行
display: flex;
.every_content{
height:150px;
margin-right: 25px;
margin-bottom: 10px;
text-align: center;
}
}
}
}
}
}
</style>
实现一个固定区域内竖向滚动(不隐藏滚动条)
<div class="srollContent">
<ul>
<li>......</li>
<li>......</li>
</ul>
</div>
<style scoped lang="less">
.scrollContent{
height:300px; //固定父盒子高度
overflow-y:scroll; //超出部分会以滚动条形式展示
}
</style>
实现一个固定区域内竖向滚动(隐藏滚动条)
<div class="content"> //一级盒子
<div class="srollContent"> //二级盒子
<ul> //三级盒子
<li>......</li>
<li>......</li>
<li>......</li>
</ul>
</div>
</div>
<style scoped lang="less">
.content{
width:300px; //一级盒子固定宽度
overflow:hidden; //超出部分隐藏
.scrollContent{
width:500px; //二级盒子固定宽度,此宽度要比一级盒子大至少50px
height:300px; //二级盒子设置固定高度
overflow-y:scroll; // 二级盒子超出部分会以滚动条形式展示
ul{
width:300px; //三级盒子固定宽度,此宽度要比一级盒子小或相等
}
}
}
</style>
原理:滚动条在二级盒子超出一级盒子之外的部分。
这里一级盒子300px,二级500px,滚动条在200px那块区间中
安利一个不错的叙述vue移动端经验的博客
https://www.cnblogs.com/wdlhao/p/9393539.html
v-if简单使用
<li class="every_content" v-for="(item,index) in heros" :key="index">
<i v-if="item.type==0">我是亚索</i>
<i v-else-if="item.type==1">我是剑姬</i>
<i v-else-if="item.type==2">我是卡萨</i>
<i v-else-if="item.type==3">我是狮子狗</i>
</li>
左侧图片右侧文字布局:图片宽度固定的,现在要实现当屏幕宽度改变时,右侧文字描述部分宽度自适应。
1、使用flex布局可以解决左右布局宽度自适应问题
2、但是flex布局时,若有文字溢出时展示省略号的需求,则需要配合overflow:hidden才可以
示例:
<div class="body">
<ul class="bodyContent">
<li class="everyContent" v-for="(item,index) in hotProductInfo" :key="index">
<div class="contentImg"><img :src="item.pictures"/></div> <!--左侧图片-->
<div class="contentDetails"> <!--右侧文字描述-->
<div class="intro">{{item.intro}}</div>
</div>
</li>
</ul>
</div>
<style scoped lang="less">
.body{
.bodyContent{
.everyContent{
display: flex; //1、flex布局
.contentImg{
margin-right: 10px;
img{
width:140px;
height:80px;
}
}
.contentDetails{
flex:auto; //2、flex:1,auto都可以。右侧文字描述部分宽度自适应
overflow: hidden; //3、这里使用overflow:hidden,解决"文字溢出省略号展示功能"失效的问题
.intro{
font-size: 16px;
color:#333333;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
}
</style>
Vue中使用vant-ui
1、安装Vant-ui yarn add vant & npm install vant -D
2、安装按需引入的babel插件 yarn add babel-plugin-import & npm install babel-plugin-import -D
3、在.babelrc文件(此文件在根目录下)中添加配置
{
"plugins": [
"transform-vue-jsx",
"transform-runtime",
["import", { //将此代码添加进plugins里
"libraryName": "vant",
"libraryDirectory": "es",
"style": true
}]
]
}
4、在main.js中按需引入
import {Button,Tag} from 'vant'; //注意import {Button,Tag} from 'vant'要在Vue.use(Button).use(Tag)之前。即 使用之前先引入
Vue.use(Button).use(Tag);
5、按照vant官网api使用即可
<van-button type="primary">按钮</van-button>
配合vant-ui框架的List组件实现“上拉加载更多”功能 (懒加载)
1、引入List组件(上面有介绍,句不过多赘述了)
2、如何使用:看下方代码
<template>
<div>
<!--【***①组件***】van-list用于监听滚动事件及设置加载相关事宜-->
<van-list
v-model="loading"
:finished="finished"
finished-text="-- 没有更多了 --"
@load="onLoad"
:immediate-check=false //初始化页面时不检查滚动位置
>
<!--这里写html代码-->
<ul class="universityInfo" >
<li class="everyInfo" v-for="(item,index) in universityList" :key="index">
<div class="infoName"><span>{{item.pname}}</span></div>
</li>
</ul>
</van-list>
</div>
</template>
<script>
import * as axios from 'axios'
import common from '../../kits/common.js'
export default {
name:"pUniversityList",
data(){
//【***②参数***】
return{
universityList:[], //请求到的数据
loading: false, //加载状态
finished: false, //是否全部加载完毕
page:1, //接口的请求页码
pagesize:15, //每次请求数据数量,在设置:immediate-check=false后,pagesize最好设置大一点
}
},
created(){
this.getUniversityList();
},
methods:{
getUniversityList(){
axios.get(`${common.coper}/allCooperation?page=${this.page}&pagesize=${this.pagesize}`) //【***③请求***】动态设置请求的页码和数据数量
.then(res=>{
if(res.data.code==200){
this.loading = false //请求接口成功时设置加载状态为false,【敲黑板,这里踩坑了,this.loading=false应该写在这里见下方解释】
if(res.data.data.list){ //因为不确定当没有数据时,后端接口返回给你的data.list是[],还是null,严谨一点,这里最好做一个判断。
this.universityList=this.universityList.concat(res.data.data.list); //将请求的数据合并到之前的数据上
//this.universityList=res.data.data.list.reverse().concat(this.universityList); //使数据倒叙排序
if(res.data.data.list.length<this.pagesize){ //当请求到的数据量小于请求的数据量时,说明数据已全部请求完毕
this.finished = true; //数据全部加载完毕
}
}else{
this.finished = true;
}
}
}).catch(err=>{})
}
//懒加载函数【***④函数***】
onLoad() {
let times=setTimeout(()=>{
this.page+=1 //每请求一次,页面数+1
this.getUniversityList()
/*this.loading=false*/
//之前的this.loading=false是写在这里的,导致了一个bug就是每次上拉页面onload函数会执行两次或多次。原因在于这里是异步执行,故this.loading=false不会立即执行,因此loading是true状态,函数onload也一直在执行中。此时就会出现多次调用接口的情况。所以要把this.loading=false设置在接口函数(见上方)中。保证在接口调用成功后,this.loading=false,即加载完成。
clearTimeout(times) //执行完定时器后清除定时器
},500)
}
},
}
</script>
上拉加载项目使用模板
【①】html模板
<van-list
v-model="loading"
:finished="finished"
finished-text="-- 没有更多了 --"
@load="onLoad"
:immediate-check=false
>
。。。。。。
</van-list>
【②】data参数设置
data(){
//【***②参数***】
return{
universityList:[],
loading: false,
finished: false,
page:1,
pagesize:15,
}
},
【③】请求接口动态设置page,pagesize等
getUniversityList(){
axios.get(`${common.coper}/allCooperation?page=${this.page}&pagesize=${this.pagesize}`)
.then(res=>{
if(res.data.code==200){
this.loading = false
if(res.data.data.list){
this.universityList=this.universityList.concat(res.data.data.list);
if(res.data.data.list.length<this.pagesize){
this.finished = true;
}
}else{
this.finished = true;
}
}
}).catch(err=>{})
},
【④】onload加载函数
onLoad() {
let times=setTimeout(()=>{
this.page+=1
this.getUniversityList()
clearTimeout(times)
},500)
}
路由传值,取值(一个参数)
1、路由部分配置:如router.js页面,在需要取值的页面的路由上添加/:testid
{
path:'/pUniversity/pUniversityInfo/:testid', //添加/:testid
component:pUniversityInfo
},
2、传值,在需要传值的页面(如列表页)
<li class="everyInfo" v-for="(item,index) in universityList" :key="index">
<router-link :to="'/pUniversity/pUniversityInfo/'+item.testid" > //这里通过这种方式传入id值,注意to需要v-bind绑定,另外注意 / 符号别少了
<div class="infoImg"><img :src="item.pimage" /></div>
<div class="infoName"><span>{{item.pname}}</span></div>
</router-link>
</li>
3、取值,在需要取值的页面(如详情页)
data(){
return{
id:this.$route.params.testid //注意这里是$route而不是$router
}
},
路由传值取值(多个参数,params方式)
1、路由页面
{
path:'/video/videoScreen',
component:VideoScreen,
name:'VideoScreen', //params传多个参数必须要给路由命名,再通过命名方式跳转。
},
2、传值页面
methods:{
postData(){
this.$router.push({name:'VideoScreen',params:{cid:this.demoCid,sid:this.demoSid,eid:this.demoEid}}) //params传多个值,只能通过name命名方式跳转,不可通过path路径方式
},
}
3、取值页面
data(){
return{
getData:this.$route.params //取值
}
}
丶丶丶【敲黑板】params通过路由name传值的方式有个缺点,就是在取值页面刷新后,传过来的值便被清空了。丶丶丶
4、上述问题解决办法:
在路由页面做如下改动
{
path:'/video/videoScreen/:cid/:sid/:eid', //这里在路由后面配置一下需要传的参数即可,这样参数就跟着路由被带过来,不会被清空了
component:VideoScreen,
name:'VideoScreen',
},
路由传值取值(多个参数,query方式)
1、路由页面
{
path:'/video/videoScreen',
component:VideoScreen,
},
2、传值页面
methods:{
postData(){
this.$router.push({path:'/video/videoScreen',query:{cid:this.demoCid,sid:this.demoSid,eid:this.demoEid}}) //query传多个值,只能通过path路径方式跳转,不可通过name命名方式
},
}
3、取值页面
data(){
return{
getData:this.$route.query//取值
}
}
Vue中使用三目运算符
【1、直接使用】
<template>
<div>{{transIndex==1?"第一个":"第二个"}}</div>
</template>
<script>
export default{
data(){
return{
transIndex:1,
}
}
}
</script>
【2、在class中使用,用来动态绑定样式】
<template>
<div>
<button id="1" class="start" :class="selectid==1?'active':''" @click="select($event)">开始</button> //注意这里active是属性,要加引号,不然会报错
<button id="2" class="stop" :class="selectid==2?'active':''" @click="select($event)">结束</button> //在class里使用三木运算符时class需要v-bind绑定
</div>
</template>
<script>
export default{
data(){
return{
selectid:1,
}
},
methods:{
select(e){
this.selectid=e.currentTarget.id //获取当前元素的id值
}
}
}
</script>
vue通过js设置当前页面最小高度等于屏幕高度
在mounted钩子函数中设置
mounted(){
document.getElementById('demo').style.minHeight = window.innerHeight + 'px';
},
vant-ui动态设值
<template>
<div>
<van-popup v-model="menuShow" position="left" :duration="state" class="sideBarPop"> //这是vantui里的弹出层组件,duration不可直接设值(需要绑定才行),需要通过:duration="state"这种方式动态赋值,或者直接:duration="0.5"
<div>DEMO</div>
</van-popup>
</div>
</template>
<script>
export default{
data(){
state:0.5,
}
}
</script>
Vue中dom元素完全渲染完毕后再获取一些数据--例子:文本超过三行后显示展开收起功能。(知识点较多)
1、完全渲染完毕后获取数据:watch对象配合Vue.nextTick方法
2、判断文本是否超过三行:获取文本高度,获取行高,行数=文本高/行高
【dom,css部分代码】
<template>
<div class="contentIntro">
<div class="intro" id="intro"><span>{{goodsList.info}}</span></div> //文本域
<div v-show="isOpenShow"> //是否显示展开收起模块
<span class="moreIntro" @click="openIntro" v-show="!isOpen">展开</span>
<span class="moreIntro" @click="closeIntro" v-show="isOpen">收起</span>
</div>
</div>
</template>
<style scoped lang="less">
.intro{
color:#6f6f6f;
font-size: 15px;
line-height: 22px; //一定要设置line-height,以便js部分获取line-height数据
//下面设置超出部分用省略号表示
display: -webkit-box; //将此display改变即可使省略失效
-webkit-line-clamp: 4; //设定行数
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.moreIntro{
float:right;
font-size: 15px;
color:#ff7700;
}
</style>
【script部分代码】
<script>
import common from '../../common.js'
import * as axios from 'axios
data(){
isOpenShow:false, //是否显示展开收起按钮
personInfo:[], //个人信息
personIntro:null, //个人信息-描述
},
watch:{ //watch配合nextTick实现dom渲染完毕后监听dom元素
personIntro:function(){
this.$nextTick(()=>{ //vue的nextTick方法,可以配合watch实现在对更新过后的dom的监听。这里是要监听【渲染完毕后】的文本高度和行高,所以使用这种方法
const elem=document.getElementById('intro')
const H=elem.offsetHeight //获取整个文本域的高度
/* const h=elem.style.lineHeight */ //获取文本的行高。这种方法只能获取行内样式里的样式,无法获取外联样式设置的样式,有局限性,这里不适用。但使用方式简单,有时可以用用。在移动端,内联样式是固定的,不会被编译器转化成rem等,没法自动适配屏幕,所以这里我们要获取外联样式,这样才能达到适配的目的,此方法只能获取内联样式是不行的。
const h=window.getComputedStyle(elem,null).getPropertyValue("line-height") //获取文本的行高。window.getComputedStyle()方法可以获取元素的样式属性,包括外联和内联样式,局限性很小。
const lineNum=parseFloat(H/parseFloat(h)).toFixed(2) //因为h的结果是带px单位的,所以这里要 parseFloat(h).toFixed(2) 转换为数字形式并保留两位小数。 lineNum=H/h的意思是行数=文本高度/行高
console.log(H)
console.log(h)
console.log(lineNum)
if(lineNum>3){ //行高大于3时,展开按钮出现,若移动端不出现展开按钮,可以将3设置小一点试试,如2.6,2.7
this.isOpenShow=true
}
})
}
},
methods:{
getPersonInfo(){
axios.get(`${common.personindex}/getCooperation?pid=${this.pid}`).then(res=>{
if(res.data.code==200){
this.personInfo=res.data.data
this.personIntro=res.data.data.pdetails //给personIntrof赋值以配合watch方法实现监听
}
})
},
openIntro(){
this.isOpen=true //收起按钮出现
document.getElementById("intro").style.display="block" //设置display,使得文本省略失效,从而展示所有文本
},
closeIntro(){
this.isOpen=false //展开按钮出现
document.getElementById("intro").style.display="-webkit-box" //重新设置display,以...省略形式展示
}
}
</script>
给v-html中的标签添加css样式
解决方案1:在updated生命周期函数中,js动态配置样式,代码如下
updated() {
$('.msgHtmlBox').find('p').css('color', 'blue');
},
解决方案2:去掉style标签中的scoped属性
scoped属性导致css仅对当前组件生效(用css3的属性选择器+生成的随机属性实现的),而html绑定渲染出的内容可以理解为是子组件的内容,子组件不会被加上对应的属性,所以不会应用css.
解决方案3:写样式的时候添加 >>> 符号(注:若使用了less,则不能使用此方法)
<style scoped>
.msgHtmlBox >>> p{
color: blue;
}
</style>
input获取焦点失去焦点(注意浏览器兼容问题)
原则上失去焦点是@blur,获取焦点是@focus
但是苹果safari浏览器不能很好的兼容@focus,所以移动端的话@focus最好用@click代替
js使input框获取焦点document.getElementById("inputName").focus()
js使input框失去焦点document.getElementById("inputName").blur()
vant-ui里toast的使用
sendComment(){
this.$toast({
message:'评论发表成功',
duration:2000,
})
this.commentValue=''
this.inputSelect=false
},
input输入框在呼出键盘后错位
https://www.cnblogs.com/ljx20180807/p/9837748.html
图片间隔处理方法(如5张图片,屏幕一行放下2张,中间间隔10px,两边无间隔)
使用nth-child(odd),nth-child(2n)方式添加margin-right:10px样式
注意看下方图片,一个是中间有间隔,两边无间隔。一个是所有图片右侧均有间隔
实现短信验证码倒计时
<span v-show="show" @click="getCode">获取验证码</span>
<span v-show="!show" class="count">{{count}} s</span>
data(){
return {
show: true,
count: '',
timer: null,
}
},
methods:{
getCode(){
const TIME_COUNT = 60;
if (!this.timer) {
this.count = TIME_COUNT;
this.show = false;
this.timer = setInterval(() => {
if (this.count > 0 && this.count <= TIME_COUNT) {
this.count--;
} else {
this.show = true;
clearInterval(this.timer);
this.timer = null;
}
}, 1000)
}
}
}
vue正则匹配(踩坑,不要用全局g)
inputuname() {
const pattern = /^[\w\u4e00-\u9fa5]{3,8}$/ //注意这里不要用g全局匹配,否则若其他地方有不符合该正则的,则表达式会一直返回false。
if (this.username.length==0) {
this.errname = false
}else if(pattern.test(this.username)==false){
this.errname = true
}else if(pattern.test(this.username)==true){
this.errname = false
}
},
input输入框光标高度问题
IE:不管该行有没有文字,光标高度与font-size大小一致
FF:该行没有文字时,光标大小与input的 height 大小一致;该行有文字时,光标大小与font-size大小一致
chrome:有2种情况,①设置了line-height 该行没有文字时,光标大小与input的 line-height 大小一致; 该行有文字时,光标大小从input顶部到文字底部
②没有设置line-height 光标大小与font-size一样
所以,input在使用的时候,最好不要设line-height, 可以设定一个较小的height, 然后用 padding 来撑开,这样基本上可以解决所有浏览器的问题
input{
height: 10px;
padding: 10px 0px;
font-size: 12px;
}
使用vant的upload组件上传头像,图片(上传图片使用form表单形式,而非json)
<template>
<div class="image">
<span>头像:</span>
<van-uploader class="imgDiv" :after-read="afterRead">
<div class="uploaderStyle" v-if="!imgUrl">
<span>+</span>
<span>上传头像</span>
</div>
<div class="imageShow" v-if="imgUrl">
<img :src="imgUrl"/>
</div>
</van-uploader>
</div>
</template>
<script>
afterRead(file){
/* console.log(file.file) */
let param = new FormData(); //创建formData对象
param.append('file', file.file); //组件获取到的图片file有时需要经过处理后上传,这里需要上传file里面的file数据,所以file.file,见下图
let config = {
headers: {
'Content-Type': 'multipart/form-data' //设置请求头格式
}
};
axios.post(`${common.api}/upload`, param, config).then(res => { //上传至 图片处理接口 拿到后台处理过后的图片url
if(res.data.code==200){
this.imgUrl=res.data.msg //这就是后台处理过后的url
axios.post(`${api.userApi}/demoimg?faceimg=${this.imgUrl}`).then(res=>{ //将处理过后的图片url上传至其他接口
if(res.data.code==200){
this.$toast("图片上传成功")
}
})
}
})
}
</script>
上传图片小结
这里上传的方式是在vant组件的after-read文件读取的钩子函数里拿到图片file,然后将file.file上传到后台的图片处理接口,再拿到处理接口返回的url,这时图片就转化成了url形式,再将这个url上传给其他需要的接口即可。
即after-read读取文件-->file传处理接口-->拿到处理后url-->使用url传其他接口
而需要说明的是并不是每个ui框架的上传组件都是这种处理模式,有的框架的文件读取是单独出来的api(如vant的after-read)。
而有的文件读取是内嵌在组件内部的,比如antDesign的文件读取就在beforeUpload之中,element,mint,cube等每一个都有自己的模式。
如果框架没有单独的文件读取api,可以参考 https://www.cnblogs.com/huihuihero/p/11060531.html 里的图片上传方式
vue根据不同数据动态设置背景
<div class="demo" :style="{background:'url('+require('../../assets/image/pic_'+(index+1)+'.png')+') no-repeat center'}"></div>
注意:图片必须要以pic_1.png pic_2.png pic_3.png pic_4.png这种数字型命名