vue学习笔记

vue 学习笔记

router-link中传值的三种方式

  • 环境搭建

vue2+webpack4+scss

以下代码中,如果传递参数

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

vuejs中使用echart图表
饼形图实例

动态组件

  • 简单用法

动态组件,使用以下标签进行绑定,其中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的例子

Vue + Mint-ui 封装滚轮选择器

全局引入第三方库

在引入underscore的库时候,遇到了这样的一个需求,需要全局引入一个库。但是在main.js中按如下代码方式引入,结果发现并不行。

参考:
Vue 中如何引入第三方 JS 库

canvas清空画布的方法

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。模块,用来组织多个组件共享的方式。分模块。