Element-UI:缓存Tabs打开的页签

竟然不缓存?

在使用Element ui 的时候,发现Tabs挺好用,于是做了这样一个页面:左侧是菜单,右侧顶部是Tabs,右侧下方是iframe.一切就绪,问题出现,就是每次点菜单的时候,Tabs就会新增一个页签,这导致iframe增加不少.于是想了个办法,来解决这个问题.

  1. 在menu菜单中,index直接为页面的名称;
  2. 在Select事件中,拿到index就可以请求页面了;
  3. 请求页面的时候,先判断页面是否已经存在于缓存中.如果存在则直接激活该页签,不存在就先添加到Tabs,然后激活,并放入缓存中.
  4. over……

但这样,存在另外一个问题:如果有几十个菜单,会堆叠几十个iframe.侧面一想,如果共用一个iframe,那么会出现另外一个问题,就是可能在上一个页面只输入了一部分文本,还没处理就切换页签,会丢失上个页面的数据.

于是,在菜单量不多的情况下,还是每个页签一个单独的iframe.便于处理.

html

源码

只有main.html是有实质内容的,其余html都是为了演示数据.

代码可以直接在本地运行,可以保存成文件,进行运行.

main.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>系统管理</title>
    <link href="https://cdn.bootcss.com/element-ui/1.4.0/theme-default/index.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
    <script src="https://cdn.bootcss.com/element-ui/1.4.0/index.js"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        .left-menu {
            width: 200px;
        }
    </style>
</head>

<body>
    <h1 class="title">系统管理</h1>
    <div id="app">
        <template>
<el-row>
  <el-col :span="4"><div class="grid-content bg-purple">
<el-menu default-active="userList" class="left-menu" @select="menuSelect" :collapse="isCollapse">
  <el-submenu index="1">
    <template slot="title">
      <span slot="title">用户</span>
    </template>
        <el-menu-item-group>
            <el-menu-item index="userList">用户列表</el-menu-item>
            <el-menu-item index="userAdd">用户添加</el-menu-item>
        </el-menu-item-group>
        </el-submenu>
        <el-submenu index="2">
            <template slot="title">
      <span slot="title">权限</span>
    </template>
            <el-menu-item-group>
                <el-menu-item index="roleList">权限列表</el-menu-item>
                <el-menu-item index="roleAdd">权限添加</el-menu-item>
            </el-menu-item-group>
        </el-submenu>
        </el-menu>
    </div>
    </el-col>
    <el-col :span="20">
        <div class="grid-content bg-purple-light">
            <el-tabs v-model="editableTabsValue" closable @tab-remove="removeTab" type="card">
                <el-tab-pane :key="item.name" v-for="(item, index) in editableTabs"  :label="item.title" :name="item.name">
                    <iframe class="iframepage" :src="item.content" width="100%" onload="changeFrameHeight()" style="border: none;" height="100%"></iframe>
                </el-tab-pane>
            </el-tabs>
        </div>
    </el-col>
    </el-row>

    </template>
    </div>
    <script>
        Vue.config.devtools = true;
        var Main = {
            data() {
                return {
                    isCollapse: false,
                    editableTabsValue: '1',
                    editableTabs: [{
                        title: '用户列表',
                        name: '1',
                        content: 'userList.html',
                        closable: false
                    }],
                    tabInfoObj: {}, // 缓存tabs打开的页签.
                    tabIndex: 1
                }
            },
            methods: { //给自定义的组件绑定事件
                selectMenu(key) {
                    var title = '';
                    switch (key) {
                        case 'userList':
                            title = '用户列表';
                            break;
                        case 'userAdd':
                            title = '用户添加';
                            break;
                        case 'roleList':
                            title = '权限列表';
                            break;
                        case 'roleAdd':
                            title = '权限添加';
                            break;
                    }
                    return title;
                },
                menuSelect(key, keyPath) {
                    if (this.tabInfoObj[key]) { // 判断,如果页签已经打开,则直接切换.
                        this.editableTabsValue = this.tabInfoObj[key] + '';
                    } else { // 如果页面未打开,则直接打开并添加到缓存对象中.
                        this.tabIndex += 1;
                        this.tabInfoObj[key] = this.tabIndex;
                        let newTabName = this.tabIndex + '';
                        this.editableTabs.push({
                            title: this.selectMenu(key),
                            name: newTabName,
                            content: key + '.html'
                        });
                        this.editableTabsValue = newTabName;
                    }
                },removeTab(targetName) {
                                // 关闭页签的时候,同时将其从缓存中删除(否则将导致后面不能再次添加).
                                for (var a in this.tabInfoObj) {
                		    if(this.tabInfoObj[a]==targetName){
                			delete this.tabInfoObj[a];
                			break;
                		   }
                	        };
			        let tabs = this.editableTabs;
			        let activeName = this.editableTabsValue;
			        if (activeName === targetName) {
			          tabs.forEach((tab, index) => {
			            if (tab.name === targetName) {
			              let nextTab = tabs[index + 1] || tabs[index - 1];
			              if (nextTab) {
			                activeName = nextTab.name;
			              }
			            }
			          });
			        }
			        this.editableTabsValue = activeName;
			        this.editableTabs = tabs.filter(tab => tab.name !== targetName);
			      }  
            },
            created: function() {
            	// 把第一个页面添加到缓存中.
                this.tabInfoObj.userList = '1';
            }
        };
        var Ctor = Vue.extend(Main)
        new Ctor().$mount('#app')


        // 设置iframe高度自适应.
        function changeFrameHeight() {
            var ifm = document.getElementsByClassName("iframepage");
            for (var i = ifm.length - 1; i >= 0; i--) {
                ifm[i].height = document.documentElement.clientHeight;
            };
        }
        window.onresize = function() {
            changeFrameHeight();
        }
        // 设置iframe高度自适应 end.
    </script>
</body>

</html>

roleList.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>权限列表</title>
</head>
<body>
    权限列表
</body>
</html>

userAdd.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>用户添加</title>
</head>
<body>
    用户添加
</body>
</html>

userList.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>用户列表</title>
</head>
<body>
    用户列表
</body>
</html>

roleAdd.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>权限添加</title>
</head>
<body>
    权限添加
</body>
</html>

读书:《深入浅出MyBatis技术原理与实战》

介绍

在不少项目中使用过MyBatis,但一直没机会深入了解过其相关知识.这次获知这本书之后,终于得以深入了解其运行原理.因原书并未附带相关随书源码.因此在看书时,非常不方便.本文后面会附上我看书时整理的源码一份(mybatis-spring 整合,及最后一章未整理),所有源码均可运行.我的环境是JDK1.8,Idea 2017.1,MySQL5.7,关于书籍信息如下:

  • 书名:深入浅出MyBatis技术原理与实战
  • 作 者:杨开振
  • ISBN:978-7-121-29594-2

mybatis logo

笔记

下面是一些笔记,供以后参考.

MyBatis基本构成:

SqlSessionFactoryBuilder(构造器):它会根据配置信息或者代码来生成SqlSessionFactory(工厂接口).
SqlSessionFactory:依靠工厂来生成SqlSession(会话).
SqlSession:是一个既可以发送SQL去执行并返回结果,也可以获取Mapper的接口.
SQL Mapper:它是MyBatis新设计的组件,它是由一个Java接口和XML文件(或注解)构成的,需要给出对应的SQL和映射规则.它负责发送SQL去执行,并返回结果.

MyBatis配置XML文件层次结构(顺序不能变)

<?xml version="1.0" encoding="UTF-8" ?>
<configuration><!-- 配置 -->
	<properties/><!-- 属性 -->
	<settings/><!-- 设置 -->
	<typeAliases/><!-- 类型命名 -->
	<typeHandlers/><!-- 类型处理器 -->
	<objectFactory/><!-- 对象工厂 -->
	<plugins/><!-- 插件 -->
	<environments><!-- 配置环境 -->
		<environment><!-- 环境变量 -->
			<transactionManager/><!-- 事务管理器 -->
			<dataSource/><!-- 数据源 -->
		</environment>
	</environments>
	<databaseIdProvider/><!-- 数据库厂商标识 -->
	<mappers/><!-- 映射器 -->
</configuration>

配置示例(application.properties)

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC
username=root
password=123456

需要配合上面的文件使用.

<!-- mybatis-config.xml -->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
		PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
		"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="application.properties">
	</properties>
	<!-- 定义别名 -->
	<typeAliases>
		<typeAlias alias="role" type="com.learn.chapter2.po.Role"/>
	</typeAliases>
	<!-- 定义数据库信息.默认使用development数据库构建环境 -->
	<environments default="development">
		<environment id="development">
			<!-- 采用jdbc事务管理 -->
			<transactionManager type="JDBC"/>
			<dataSource type="POOLED">
				<property name="driver" value="${driver}"/>
				<property name="url" value="${url}"/>
				<property name="username" value="${username}"/>
				<property name="password" value="${password}"/>
				<!--<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTimezone=UTC"/>
				<property name="username" value="root"/>
				<property name="password" value="123456"/>-->
			</dataSource>
		</environment>
	</environments>

	<!-- 定义映射器 -->
	<mappers>
		<mapper resource="com\learn\chapter2\mapper\roleMapper.xml"/>
	</mappers>
</configuration>

maven如果不能读取xml或者properties配置文件?

<!-- 在pom.xml中添加下面的内容 -->
<build>
		<resources>
			<resource>
				<directory>src</directory>
				<includes>
					<include>**/*.xml</include>
					<include>**/*.properties</include>
				</includes>
				<filtering>true</filtering>
			</resource>
		</resources>
	</build>

首选配置properties文件方式进行配置.

MyBatis映射器主要元素

特殊字符串的替换和处理(#和&)

columns = “c1,c2,c3″ #{columns} 会直接显示”c1,c2,c3”,会对参数进行处理.

${columns} 不会处理参数,会直接显示.例如(c1,c2,c3)

需要注意SQL注入的问题.

常用工具类

package xyz.suancaiyu.xyz.suancaiyu.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;

/**
 * Created by puruidong on 2017/6/21.
 */
public class SqlSessionFactoryUtil {

    //SqlSessionFactory对象
    private static SqlSessionFactory sqlSessionFactory = null;
    //类线程锁
    private static final Class CLASS_LOCK = SqlSessionFactoryUtil.class;

    private SqlSessionFactoryUtil() {
    }

    public static SqlSessionFactory getInstance() {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            java.util.logging.Logger.getLogger(SqlSessionFactoryUtil
                    .class.getName()).log(Level.SEVERE, null, e);
        }
        synchronized (CLASS_LOCK) {
            if (sqlSessionFactory == null) {
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }
        }
        return sqlSessionFactory;
    }

    public static SqlSession openSqlSession() {
        if (sqlSessionFactory == null) {
            getInstance();
        }
        return sqlSessionFactory.openSession();
    }


}

二级缓存默认不开启. 开启需要在映射文件中(查询数据中),添加

即可开启二级缓存.

元素 作用 备注
if 判断语句 单条件分支判断
choose(when,otherwise) 相当于Java中的if else语句 多条件分支判断
trim(where,set) 辅助元素 用于处理一些SQL拼接问题
foreach 循环语句 在in语句等列举条件常用

映射器的组成

MappedStatement:它保存映射器的一个节点(select|insert|delete|update),包括许多我们配置的SQL,SQL的id,缓存信息,resultMap,parameterType,resultType,languageDriver等重要配置内容;

SqlSource:它是提供BoundSql对象的地方,它是MappedStatement的一个属性.

BoundSql:它是简历SQL和参数的地方.它有3个常用的属性:SQL,parameterObject,parameterMappings.

1.Executor是执行SQL的全过程,包括组装参数,组装结果集返回和执行SQL过程,都可以拦截;
2.StatementHandler是执行SQL的过程,我们可以重写执行SQL的过程;
3.ParameterHandler,拦截执行SQL的参数组装;
4.ResultSetHandler用于拦截执行结果的组装.

源码及使用

使用源码可遵循如下步骤:

  1. 配置JDK1.8环境
  2. 配置MySQL5.6
  3. 配置Maven或Gradle
  4. 配置IDEA,并将源码包中的mybatis.sql导入到数据库中
  5. over…

贴一个maven的配置(使用了阿里的maven仓库,速度杠杠的).

<!-- settings.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>F:\maven_repository</localRepository>
  <pluginGroups>
  </pluginGroups>
  <proxies>
  </proxies>
  <servers>
  </servers>
  <mirrors>
    <mirror>
        <id>nexus-aliyun</id>
        <mirrorOf>*</mirrorOf>
        <name>Nexus aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror> 
  </mirrors>
  <profiles>
  </profiles>
</settings>

源码下载.mybatils 源码