vue学习笔记
vue 学习笔记
- 环境搭建
以下代码中,如果传递参数
1为对应的学生的id?
<li v-for="student in students" :key="student.id" @click="queryScore(1)">
<img src="../../assets/1.png" alt="">
<p>{{student.name}}</p>
</li>
使用图标展示数据
引入库文件
npm install echarts --save
动态组件
- 简单用法
动态组件,使用以下标签进行绑定,其中which_to_show指定绑定的组件的名称。然后在其他处动态的改变这个值,即能达到切换效果。
<component v-bind:is="which_to_show"></component>
- 动态组件,监听子组件传递的消息
跟普通的组件传递消息一致。
父组件监听消息
<component v-bind:is="dayRecordShow" v-on:dayforward="dayRecordShow='DayList'"></component>
子组件通过按钮,触发消息
<mt-button type="primary" size="large" @click="$emit('dayforward')">查看详情</mt-button>
v-for使用
v-for动态选择,然后根据选中的id动态设置选择的样式class。实现了动态选择选项的功能。
//模板
<div class="stuclass">
<ul>
<li
v-for="classInfo in classList" :key='classInfo.cid'
:class="classInfo.cid==selClass?'active':'deactive'"
@click="selClass=classInfo.cid"
>
{{classInfo.className}}
</li>
</ul>
</div>
//数据
data() {
return {
selClass:1,
classList:[
{cid:1,className:'一班'},
{cid:2,className:'二班'},
{cid:3,className:'三班'},
{cid:4,className:'四班'},
{cid:5,className:'五班'},
{cid:6,className:'六班'},
{cid:7,className:'七班'}
],
chart: null,
};
},
MintUI日期选择控件使用
参照官方的例子,确实比较简单。简单的使用例子如下datetime-picker
注意,不要将此组件,放到触发的组件容器中,否则,无限弹出。
//引入日期时间选择器。 下面进行全局注册
import { DatetimePicker } from 'mint-ui';
Vue.component(DatetimePicker.name, DatetimePicker);
//
增加字体
将font-awesome下载后,放到/src/static/font-awesome文件夹下。(css、font两个文件夹,其他的不要。)
//在main.js中直接引用以下样式
import './static/font-awesome/css/font-awesome.min.css';
在代码中,即可使用。如:
<i class="fa fa-angle-left" aria-hidden="true"></i>
MintUI封装picker的例子
全局引入第三方库
在引入underscore的库时候,遇到了这样的一个需求,需要全局引入一个库。但是在main.js中按如下代码方式引入,结果发现并不行。
canvas清空画布的方法
- 最简单的方法:由于canvas每当高度或宽度被重设时,画布内容就会被清空,因此可以用以下方法清空
function clearCanvas()
{
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
c.height=c.height;
}
- 使用clearRect方法
function clearCanvas()
{
var cxt=document.getElementById("myCanvas").getContext("2d");
cxt.clearRect(0,0,c.width,c.height);
}
引用mint-ui中的swipe
引入到文件中,看不到任何效果,查看dom节点,发现有结构。所以,是因为div没有默认的高度,所以呢,设置高度即可显示。如果是图片,则会为img增加widh=100%。
div单行显示
如果想让div单行显示,其实只要设置行高跟div的高度一致,然后多余的文字使用overflow:hidden即可。
常用的圣杯布局、双飞布局
圣杯布局小结文章讲解比较详细。而且这个人的博客还是很不错的。
keep-alive属性
这个属性,用在动态组件上面,这样动态组件不用每次都销毁重建。(下面方式进行监听)
destroyed(){
console.log('我销毁了啦');
},
mint-ui中的切换卡片按钮
通过观察dom节点,实际上改变的是该节点的可见性。(mint-tab-container-item display:none)
也就是说,所有节点其实都已经渲染上了,但是只是没有显示而已。这个跟keep-alive动态组件还不一样。动态组建,实际上整体已经脱离了文档流,估计状态都还保存在内存中。再次需要的时候,再次放回去。原理不一样。
为什么说,动态组件,切换时dom节点会脱离文档流?因为使用了定期任务setInterval,在定时任务执行时,切换了,结果找不到了dom节点。所以,如果有定时任务,延时任务,而且还需要对dom节点操作,请不要这么干,直接使用v-show好啦。这个东西,就不会啦。2019/2/28
mint-ui中的Indicator
其功能其实跟Ext.Msg.Wait的功能很像,作用是,防止用户多次点提交按钮。所以,用法也相似。2019/2/28
Indicator.open('审核中...');
//请求成功或者失败,都应该关闭
setTimeout(function(){
Indicator.close();//关闭全局的mask
me.$router.go(-1);
},1000);
感悟
做一件事的方法,有很多种,善于思考,总能找到一种适合的解决办法。
(2019年2月28日)今天做考勤路径跳转,在做之前,已经想过了一种解决方法,就是,如果当前页面是考勤页面,默认的全局返回按钮隐藏起来,然后设置该页面特定的按钮,然后跟该组件能紧密的联合起来,能获取到相关的参数,控制特定的返回过程。
但是,中午看手册,路由地址变化,如果地址不变,只是参数发生了变化,那么,组件不会刷新。正好利用此特性,在原因的路由上面,加了请求参数。
watch: {
'$route' (to, from) {
var show = this.$route.params.show;
var dayRds = ['DaySummary', 'DayList', 'DayDetail'];
var monthRds = ['MonthSummary', 'MonthList', 'MonthDetail'];
var index = dayRds.indexOf(show);
if (index > -1) {
this.selected = 'dayRecord';
this.dayRecordShow = dayRds[index];
return;
}
index = monthRds.indexOf(show);
if (index > -1) {
this.selected = 'monthRecord';
this.monthRecordShow = monthRds[index];
}
}
},
slot
- 如果引入的组件,含有插槽,在插槽里面的内容,其能享受到该组件的所有域。
<mt-popup
v-model="popupVisible"
popup-transition="popup-fade"
>
<div class="photo-container" @click="popupVisible=false">
这个里面的所有域,跟外面的相同。
<div class="photo-img"
v-for="(photo,index) in photos"
:key="index"
>
<img src="../../assets/1.png" alt="">
</div>
</div>
</mt-popup>
使用sass文件格式
使用sass语言的时候,由于层层嵌套的原因,如果在浏览器中发现想要的样式,没有列处理,查看拼写是否错误,或者嵌套的错误。无法识别等。
使用overflow清除浮动
- div内部有float元素,该div可以使用float来清除浮动。
- 另外一种方法是,该div增加一个子div,子div style=”clear:both”
事件绑定
遇到一个如下的问题,需要循环photos里面的元素,并根据该元素绑定一个事件,但是如果photos为空的话,那么会报如下错误:
Uncaught TypeError: Cannot read property '$emit' of undefined
大概的意思是:为遍历的元素增加了触发事件,结果该遍历的条目为空。然后事件绑定就报错了。暂无解法,只能限制用户不能将photos数组清空。
<!-- 点击轮播图,查看图片详情 -->
<mt-popup
v-model="popupVisible"
position="right"
:modal="false"
>
<div class="photo-container">
<div class="photo-img"
v-for="(photo,index) in photos"
:key="index"
>
<div class="tools" @click="deletePic(photo.src)">
<i class="iconfont icon-delete"></i>
</div>
<img src="../../assets/1.png" alt="">
</div>
<div class="btn">
<mt-button type="primary" size="large" @click.native="popupVisible=false">确定</mt-button>
</div>
</div>
</mt-popup>
//删除方法
deletePic(src){
console.log('您要删除图片'+src);
var index=-1;
var ps=this.photos;
//查找到对应删除的条目
for(var i=0;i<ps.length;i++){
if(src===ps[i].src){
index=i;
break;
}
}
if(index>-1){//找到
this.photos.splice(index,1);
//发送请求,将图片从服务器上删除。
//todo
}
},
另外,发现Mint UI中Toast的z-index更小。
Toast('必须要保留一张图片。');
临时解决办法:
//条件判断,要不要显示
<div class="tools" @click="deletePic(photo.src)" v-if="photos.length>1">
<i class="iconfont icon-delete"></i>
</div>
vue中使用手势库问题
可用的库 vue-touch 还有touchjs。反正这个库不是很好用。
对于前一种库,使用如下:
//默认的生成div标签,但是问题是,滑动,swiperight,标签多了一个touch-action: none;样式,
//该样式阻止了正常上下的滑动。
//style="touch-action: pan-y!important;" 该样式能恢复,但是非常麻烦。
<v-touch v-on:swiperight="sright"></v-touch>
vue中引入图片
如果直接在html代码中引入代码,框架打包的时候,会自动的处理图片的连接。但是如果在代码中,引入的话,需要自己来控制。核心代码如下:
require("../assets/test.png");
应用举例:
//1、首先给图片地址绑定变量
<template>
<img :src="imgUrl">
</template>
//2、在script中设置变量
<script>
//方法1.直接将图片引入为模块
require imgUrl from "../assets/test.png"
//方法2.将imgUrl放在数据里
data(){
return {
imgUrl:require("../assets/test.png")
}
}
</script>
页面跳转“保活”功能
有的时候,页面跳转,返回回来时,页面要求还是上次已加载的样式。我个人称其为“保活”。在Ext中,如果采用card布局,很容易就实现了“保活”。在Vue里面,根据情况,分为三种:
- if判断:如果页面路由没有发生跳转,此页面就能实现报活。(主要利用v-if或者v-show动态的显示不同的页面)
- 参数:如果页面跳转了,但是只是路由的参数发生了变化,根据Vue官方路由中的介绍,页面不会发生重新渲染。
- 子路由:做一个空的组件,做容器。路由出口,让其保活。
- 动态组件。暂未思考。
子路由应用具体的如下:
父容器:
<template>
<!-- 父容器,让其保活,keep-alive -->
<div class="scorehome">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name: "ScoreHome",
};
</script>
子路由设置:
{
path: '/score/',
name: 'ScoreHome',
meta: { title: '成绩查询' },
component: resolve => require(['../components/score/ScoreHome'], resolve),
children: [
{
path: 'index',
name: 'Score',
meta: { title: '成绩查询' },
component: resolve => require(['../components/score/Score'], resolve),//懒加载
},
{
path: 'queryscore',
name: 'QueryScore',
meta: { title: '成绩查询' },
component: resolve => require(['../components/score/QueryScore'], resolve),//懒加载
}
]
},
这样即实现了保活功能。
但是,现实需求是复杂的,页面从1→2→3或者3→2→1这样方向传递。其中2需要保活,3不需要保活。怎么做呢?
根据上面的三种方式,后两种方式差不多。每次进入3的时候,让其主动刷新呢?那么就需要导航守位或者监控导航的变化。或者还可以利用消息传递机制。强制让一个组件重新刷新,这个?
临时解决办法
临时监控路由变化。
watch:{
$route(to,from){
var me = this;
//如果判断是当前路由,则进行初始化,并重新加载数据。
//注意,这个方法,首次并不会执行,只有再次过来才会执行。
if(to.path=="/score/queryscore"){
this.user_name= null;
this.exam_name=null; //考试名称
this.exam_type= null; //考试类型
me.studentObj = to.query.studentObj;
me.parseExam();
}
}
},
导航守位
beforeRouteEnter这个函数里面好像访问不到this,这个函数里面必须要调用next(),否则加载不到页面。它比watch里面监控到的$route变化发生更早。
关于未修改,不提示直接返回
这个是Ext后台的功能,用户希望,没有修改时,可以直接返回,不要进行提醒。思路,监控用户有无输入比较麻烦。则判断,用户点击组件的次数,如果只点了一次(这一次是发生在点返回按钮时),那么认为没有做任何修改。弊端,有可能用户用键盘发生了变化,这个就不考虑了。核心代码如下:
//增加点击时,
listeners:{
'afterrender':function(cmp, eOpts){
var me=this;
me.el.dom.onclick=function(){
me.bodyCount++;
};
},
'destroy ':function(cmp, eOpts){
this.el.dom.onclick=null;
}
},
//在每次添加、修改时,重新初始化一次。因为这个组件是不死的。
ShineMessageHub.on("roomlistgridpaneladdevent",function(rd){
me.bodyCount=0;
//在返回时,判断次数,<=1则进行默认处理。
serviceworker
第一次听说这个概念
//通过一下方式,进行控制。
chrome://serviceworker-internals/
手机端禁止缩放
代码如下,但是对于浏览器好像,并没有强制,但是对于微信,好像支持。
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1,user-scalable=no">
网上找来的解释:
该meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。 其中 maximum-scale为允许用户的最大缩放值,user-scalable为是否允许用户进行缩放,yes(默认)代表允许,no(0)代表不允许,两者结合使用可以阻止页面被放大(经测试,少一项都达不到效果)
vue中引入包的先后顺序
代码如下,错误的解释如下:
import Vue from 'vue'
//注意,这个App包先引入,所以无法使用后面引入的东西。
import App from './App'
import router from './router/index'
import Mint from 'mint-ui';
import 'mint-ui/lib/style.css'
import { Header } from 'mint-ui';
import { TabContainer, TabContainerItem } from 'mint-ui';
import { Navbar, TabItem } from 'mint-ui';
import axios from "axios"
import GlobalTools from './components/GlobalTools.js'//引用模块进来
//像下面的这个MessageBox就无法在App中直接使用。因为顺序问题。
import { MessageBox } from 'mint-ui';
import { InfiniteScroll } from 'mint-ui';
import { Loadmore } from 'mint-ui';
import { Actionsheet } from 'mint-ui';
重要的原因是:
import Mint from 'mint-ui';
//在注册到Vue中,每个实例都多了$indicator、$messagebox、$toast方法。
Vue.use(Mint);
监控返回事件
本来想做的功能是,监控返回时,判断,如果要退出了,则提示是否要退出。结果,下面的这个功能死活不起作用。(估计在调试模式下,websocket会返回的加载更改的代码)
window.addEventListener('popstate',function(){
console.log('监控退出事件');
var notShowBackList=['/','/HomePanel/','/MySelfPanel/','/SettingPanel/'];
var find=notShowBackList.indexOf(me.$router.history.current.path);
if(find>-1){
MessageBox.confirm('确定要退出吗?').then(action => {
}).catch(action=>{
window.history.pushState('forward', null, '#/');
window.history.forward(1);
});
}
},false);
格式化时间代码优化
以前使用Ext的库函数惯了,后来自己使用vue框架的时候,很多库都没有了。然后自己写了格式化时间代码。又经再次优化,如下:
/************
* 2019年2月22日
* a 时间 sep 分隔符,格式化输出时间
************/
formatDate(date, sep) {
if (!date) {
return '';
}
//改进的代码
return date.toISOString()
.replace(/T/, ' ') // replace T with a space
.replace(/\..+/, '')
.slice(0, -3);
//之前的代码
return (
date.getFullYear() +
"-" +
(date.getMonth() > 8 ? "" : "0") +
(date.getMonth() + 1) +
"-" +
(date.getDate() > 9 ? "" : "0") +
date.getDate() +
" " +
(date.getHours() > 9 ? "" : "0") +
date.getHours() +
':' +
(date.getMinutes() > 9 ? "" : "0") +
date.getMinutes());
然后最终优化为:
/************
* 2019年2月22日
* date时间对象 ,格式化输出时间
************/
//date.toISOString如果没有这个方法,那么认为不是时间格式。返回为空字符串。
//比上面的异常判断更好。
formatDate(date) {
return date&&date.toISOString?date.toISOString()
.replace(/T/, ' ') // replace T with a space
.replace(/\..+/, '')
.slice(0,-3):'';
},
然后发现,时区还不对,格始化的时间小了8小时。然后,又再次优化为:
formatDate(date) {
//运用了括号表达式,(1,2)返回最后一个值的值,1表达式,主要功能,纠正时间8小时误差。
return date&&date.toISOString?(date=new Date(date.getTime()-date.getTimezoneOffset()*60*1000),date.toISOString()
.replace(/T/, ' ') // replace T with a space
.replace(/\..+/, '')
.slice(0,-3)):'';
},
上面运用到了一个知识,Date本身是对象,所以呢,其是按址传递的。但是,date=new Date 其实相当于又拷贝了一个新对象。跟原对象已经没有关系了。所以呢,new之后,跟调用处的date,formatDate(date) ,已经脱离关系了。
至于如何验证Date传址,下面是个例子。
a= new Date();
console.log(a);
b=a;
b.setFullYear(2018);
console.log(a);
伪元素after之content
本来的想法是,after里面放“内容”之内的html,结果发现不行。
.selected::after {
content: "\e63e";
position: absolute;//能使用绝对定位
}
使用参数传递状态值
之前,从上个页面跳到页面来,上个页面直接将查询处理后结果传递过来,这样做,确实比较省事,但是问题:页面刷新,上个页面传递的数据就不存在了。
变通:将其作为参数params来传递,传递一个资源值。是不是更好一点?
vue中使用过滤器
使用过滤器,对要展示的数据进行转换,这是另外一个地方,可以对数据进行转换。扩展:
- 后端处理。Mysql的时候处理。
- 后端处理。php语言处理。
- 数据加载后,处理。Ajax后,立马组织数据。
- 计算属性,跟Ext中Store的convert属性作用差不多。
- filter处理。这个跟Ext中renderer函数有点像,这个不会更改原数据。
vuex使用
vuex的出现,主要解决的问题是,多组件之间的通信问题。在未引入vuex之前,组件的通信:父组件→子组件,通过props向子组件传递,而子组件→父组件就必须通过事件触发$emit的方式进行传递。如果兄弟组件通信,需要利用父组件进行中转,如果是更复杂的组件之间通信,方式非常麻烦。为了解决这个问题,引入了store的概念。官方手册中,讲解了可以定义一个共享的store,然后其他的组件,直接使用store的state属性来初始化变量,直接更改state并共享,这种方式,非常不利于排查问题。所以建议,所有更改必须通过一个方法来触发,这样可以跟踪变量。
vuex中共有五个概念:
- State:共享的数据。这个是所有需要通信的组件,共享此属性内的组件。(js对象变量传址方式传递值。但是,不要直接操纵变量来更改)
- Getter:提供数据转换的方法。(类似Ext中store的fields的convert方法),共享此方法,过滤、转换等作用。
- Mutaction方法。(突变)。主要用来更改状态。(不能使用延时的操作)。2019/3/11
- Action。跟Mutaction最大的区别,允许有一步操作,操作结束并不直接更改State中的数据,而是commit去调用Mutaction更改变化,返回的是Promise。
- Module。模块,用来组织多个组件共享的方式。分模块。