vue router 的命名视图

vue router 能够很好的解决页面跳转的问题。

但是以前的用法,我只会在组件里最多写一个 RouterView ,那么如何同时存在多个呢?

只需要将原来的代码

component: Home,

改成

      components: {
        default: Home,
        // LeftSidebar: LeftSidebar 的缩写
        LeftSidebar,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        RightSidebar,
      },

注意 component 变成了 components

然后这样使用

<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" name="RightSidebar"></router-view>

vue cli 使用 iconfont

已知 iconfont 是非常好用的图标库,那么如何在项目里使用它呢?

创建 Iconfont 项目

将资源引入到vue cli项目里

在 public/index.html 文件里,添加一行代码

<script src="<实际资源地址>"></script>

引入通用的css类

.icon {
    width: 1em;
    height: 1em;
    vertical-align: -0.15em;
    fill: currentColor;
    overflow: hidden;
}

使用方式

<svg class="icon" aria-hidden="true">
    <use :xlink:href="<实际ID>"></use>
  </svg>

实际ID可以通过 iconfont 后台复制

封装成组件的形式

<template>
  <svg class="icon" aria-hidden="true">
    <use :xlink:href="icon"></use>
  </svg>
</template>

<script>
export default {
  name: "IconFont",
  props: {
    icon: {
      type: String,
      required: true,
    }
  },
  created() {
    if (this.icon == undefined) {
      throw new Error("使用 iconfont 组件比如传入类名");
    }
  }
}
</script>

<style scoped>

</style>

setup 组件

<template>
  <svg class="icon-font" aria-hidden="true">
    <use :xlink:href="icon"></use>
  </svg>
</template>
<script lang="ts" setup>
const props = defineProps({
  icon: {
    type: String,
    required: true,
  },
});

if (props.icon === undefined) {
  throw new Error("使用 IconFont 组件需要传入 icon 属性");
}
</script>
<script lang="ts">
export default {
  name: "IconFont",
};
</script>

<style lang="scss" scoped>
.icon-font {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>

使用第三方组件的形式

npm官方地址 支持的是 vue2.x

安装
# install global 
npm install vue-svgicon -g
# install for project 
npm install vue-svgicon --save

iconfont 各种图标的区别

官网地址 原文地址

unicode 引用

unicode是字体在网页端最原始的应用方式,特点是:

  • 兼容性最好,支持ie6+,及所有现代浏览器。
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。

注意:新版iconfont支持多色图标,这些多色图标在unicode模式下将不能使用,如果有需求建议使用symbol的引用方式

unicode使用步骤如下:

第一步:拷贝项目下面生成的font-face
@font-face {font-family: 'iconfont';
    src: url('iconfont.eot');
    src: url('iconfont.eot?#iefix') format('embedded-opentype'),
    url('iconfont.woff') format('woff'),
    url('iconfont.ttf') format('truetype'),
    url('iconfont.svg#iconfont') format('svg');
}
第二步:定义使用iconfont的样式
.iconfont{
    font-family:"iconfont" !important;
    font-size:16px;font-style:normal;
    -webkit-font-smoothing: antialiased;
    -webkit-text-stroke-width: 0.2px;
    -moz-osx-font-smoothing: grayscale;
}
第三步:挑选相应图标并获取字体编码,应用于页面
<i class="iconfont">&#x33;</i>

font-class 引用


font-class是unicode使用方式的一种变种,主要是解决unicode书写不直观,语意不明确的问题。

与unicode使用方式相比,具有如下特点:

  • 兼容性良好,支持ie8+,及所有现代浏览器。
  • 相比于unicode语意明确,书写更直观。可以很容易分辨这个icon是什么。
  • 因为使用class来定义图标,所以当要替换图标时,只需要修改class里面的unicode引用。
  • 不过因为本质上还是使用的字体,所以多色图标还是不支持的。

使用步骤如下:

第一步:拷贝项目下面生成的fontclass代码:
//at.alicdn.com/t/font_8d5l8fzk5b87iudi.css
第二步:挑选相应图标并获取类名,应用于页面:
<i class="iconfont icon-xxx"></i>

symbol 引用


这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 这种用法其实是做了一个svg的集合,与上面两种相比具有如下特点:

  • 支持多色图标了,不再受单色限制。
  • 通过一些技巧,支持像字体那样,通过font-size,color来调整样式。
  • 兼容性较差,支持 ie9+,及现代浏览器。
  • 浏览器渲染svg的性能一般,还不如png。

使用步骤如下:

第一步:拷贝项目下面生成的symbol代码:
//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js
第二步:加入通用css代码(引入一次就行):
<style type="text/css">
    .icon {
       width: 1em; height: 1em;
       vertical-align: -0.15em;
       fill: currentColor;
       overflow: hidden;
    }
</style>
第三步:挑选相应图标并获取类名,应用于页面:
<svg class="icon" aria-hidden="true">
    <use xlink:href="#icon-xxx"></use>
</svg>

注意前面的 # 号

yarn 和 npm 命令对照

如果你会使用 npm ,只需要这样的表格,则会使用 yarn

NPMYarn说明
npm inityarn init初始化某个项目
npm install/linkyarn install/link默认安装依赖
npm install taco –saveyarn add taco安装某个依赖并默认保存到package
npm uninstall taco –saveyarn remove taco移除某个依赖
npm install taco –save -devyarn add taco -dev安装某个开发时的依赖
npm update taco –saveyarn upgrade taco更新某个依赖项目
npm install taco –globalyarn global add taco安装某个全局依赖项目
npm publish/login/logoutyarn publish/login/logout发布/登录/退出
npm run/testyarn run/test运行某个命令

Office 部署和激活

网上的激活方式五花八门,有一种通用的工具可以解决部署和激活的问题,上手加单。

官网 Office Tool Plus

部署

首先从下载地址下载包含框架的版本。

解压之后打开目录下的 Office Tool Plus.exe 程序。

进入部署界面,选择导入配置

<Configuration>
  <Add OfficeClientEdition="64" Channel="PerpetualVL2021" AllowCdnFallback="true">
    <Product ID="ProPlus2021Volume">
      <Language ID="zh-cn" />
      <ExcludeApp ID="Access" />
      <ExcludeApp ID="Lync" />
      <ExcludeApp ID="OneDrive" />
      <ExcludeApp ID="OneNote" />
      <ExcludeApp ID="Outlook" />
      <ExcludeApp ID="Publisher" />
      <ExcludeApp ID="Teams" />
    </Product>
  </Add>
  <Display AcceptEULA="True" />
  <Property Name="AutoActivate" Value="1" />
</Configuration>

文件请以 XML 后缀命名,导入之后可以更具自己需要进行自定义。

然后点击部署即可,等待部署成功,office 就安装成功了。

激活

我喜欢采用 kms 激活,更多激活方式参考官方帖子

可能会用到 kms 主机列表 来查询可用的主机,也可以从互联网上检索。

  1. 进入激活界面
  2. 根据上面的版本安装许可证,如果没有改动,选择 Office LTSC 专业增强版 2021 – 批量许可证
  3. 等待输出 产品密钥安装成功 的字样
  4. 在 KMS 管理模块中 输入KMS主机 http://kms.03k.org,也可以检查http://kms.03k.org状态
  5. 点击保存设置,点击激活按钮。激活成功会输出产品激活成功字样

激活需要注意的地方

  • 每隔 7 天(默认),Office 都会与 KMS 通信一次,以更新自己的许可证状态。
  • 如果 KMS 允许你更新许可,那么新的许可就是 180 天期限。
  • 如果 KMS 不允许你更新许可,或者 KMS 不可用,那么原来的 180 天期限到期后,你的 Office 就会变成未激活了。

redis 的基本数据类型

Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。

在 windows 下,通过命令行连接redis是用 redis-cli.exe

redis 的命令 可以参考 redis 中文网

String(字符串)

string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。

string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。

string类型是Redis最基本的数据类型,一个键最大能存储512MB

127.0.0.1:6379> set name "hello redis"
OK
127.0.0.1:6379> get name
"hello redis"

Hash(哈希)

Redis hash 是一个键值对集合。

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

127.0.0.1:6379> HMSET user name codeon password 123456
OK

h表示hash的缩写

m表示multi的缩写

注意:命令的格式是 HMSET <键名> <field1> <value1> <field2> <value2> …

每个 hash 可以存储 232 – 1 键值对(40多亿)

List(列表)

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。

127.0.0.1:6379> LPUSH domain baidu.com sougou.com
(integer) 2

命令格式: LPUSH <key> <value1> <value2> …

列表最多可存储 232 – 1 元素 (4294967295, 每个列表可存储40多亿)。

Set(集合)

Redis的Set是string类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

zset(sorted set:有序集合)

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

127.0.0.1:6379> ZADD user 1 user1
(integer) 1
127.0.0.1:6379> ZADD user 3 user3
(integer) 1
127.0.0.1:6379> ZADD user 2 user2
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE user 0 1000
1) "user1"
2) "user2"
3) "user3"

可与观察到 user 的顺序和插入的次数无关,由分数的大小从小到大进行排序。

Spring中的IOC和DI

IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”。

DI是依赖注入。

IOC 和 DI 是一个东西,只是说法不同。

Spring IOC的基本概念


控制反转(IoC)是一个比较抽象的概念,它主要用来消减计算机程序的耦合问题,是Spring框架的核心。
依赖注入(DI)是IoC的另外一种说法,只是从不同的角度描述相同的概念。

IOC 的背景

我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。

软件系统中耦合的对象

  如果我们打开机械式手表的后盖,就会看到与上面类似的情形,各个齿轮分别带动时针、分针和秒针顺时针旋转,从而在表盘上产生正确的时间。图1中描述的就是这样的一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项任务。我们可以看到,在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。

  齿轮组中齿轮之间的啮合关系,与软件系统中对象之间的耦合关系非常相似。对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础。现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。

对象之间的依赖关系

耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson 1996年提出了IOC理论,用来实现对象之间的“解耦”,目前这个理论已经被成功地应用到实践当中。

什么是IOC

  IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”。

  1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。

IOC解耦过程

大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。

我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:

拿掉IOC容器后的系统

 我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!

    我们再来看看,控制反转(IOC)到底为什么要起这么个名字?我们来对比一下:

    软件系统在没有引入IOC容器之前,如图1所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

    软件系统在引入IOC容器之后,这种情形就完全改变了,如图3所示,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。

    通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

参考文章

[1] 浅谈IOC–说清楚IOC是什么

springboot 的过滤器和拦截器

过滤器(Filter)和拦截器(Interceptor)的区别

  • spring的拦截器和servlet的过滤器有相似之处,都是AOP思想的体现,都可以实现权限检查,日志记录,不同的是
  • 适用范围不同:Filter是Servlet容器规定的,只能使用在servlet容器中,而拦截器的使用范围就大得多
  • 使用的资源不同:拦截器是属于spring的一个组件,因此可以使用spring的所有资源,对象,如service对象,数据源,事务控制等,而过滤器就不行
  • 深度不同:Filter还在servlet前后起作用。而拦截器能够深入到方法前后,异常抛出前后,因此拦截器具有更大的弹性,所有在spring框架中应该优先使用拦截器。
    通过调试可以发现,拦截器的执行过程是在过滤器的doFilter中执行的,过滤器的初始化会在项目启动时执行。

过滤器(Filter)

springboot下过滤器的使用有两种形式:

注解形式

  1. 创建一个Filter,并使用WebFilter注解进行修饰,表示该类是一个Filter,以便于启动类进行扫描的时候确认。
  2. 然后在启动类上添加注解@ServletComponentScan,该注解用于自动扫描指定包下(默认是与启动类同包下)的WebFilter/WebServlet/WebListener等特殊类。
package cn.codeon.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/*",filterName = "customFilter")
public class CustomFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

bean 的方式

package cn.codeon.filter;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;

@Component
@Order(1)
public class CustomFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter");
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

拦截器(Interceptor)

工作原理

一个拦截器,只有preHandle方法返回true,postHandle、afterCompletion才有可能被执行;如果preHandle方法返回false,则该拦截器的postHandle、afterCompletion必然不会被执行。拦截器不是Filter,却实现了Filter的功能,其原理在于:

  • 所有的拦截器(Interceptor)和处理器(Handler)都注册在HandlerMapping中。
  • Spring MVC中所有的请求都是由DispatcherServlet分发的。
  • 当请求进入DispatcherServlet.doDispatch()时候,首先会得到处理该请求的Handler(即Controller中对应的方法)以及所有拦截该请求的拦截器。拦截器就是在这里被调用开始工作的。

工作流程

正常流程:

Interceptor.preHandle >> Controller处理请求 >> Interceptor.postHandle >> 渲染视图view >> Interceptor.afterCompletion

中断流程:

如果在Interceptor1.preHandle中报错或返回false ,那么接下来的流程就会被中断,但注意被执行过的拦截器的afterCompletion仍然会执行。下图为Interceptor1.preHandle返回false的情况:

前置拦截器2 preHandle: 用户名:null

前置拦截器1 preHandle: 请求的uri为:http://localhost:8010/user/353434

拦截器2 afterCompletion:

应用场景

限制访问次数

记录用户IP访问次数,第一次访问时在redis中创建一个有效时长1秒的key,当第二次访问时key值+1,当值大于等于5时在redis中创建一个5分钟的key,当拦截器查询到reids中有当前IP的key值时返回false限制用户请求接口

  1. 日志记录,可以记录请求信息的日志,以便进行信息监控、信息统计等。
  2. 权限检查:如登陆检测,进入处理器检测是否登陆,如果没有直接返回到登陆页面。
  3. 性能监控:典型的是慢日志。 

使用方法

  • 类要实现Spring 的HandlerInterceptor 接口。
  • 类继承实现了HandlerInterceptor 接口的类,例如:已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter。
public interface HandlerInterceptor {
 
   /**
     * 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
     * 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断
(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
   */
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;
 
   /**
     * 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以
通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
   */
    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;
 
   /**
    * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输
出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
   */
    void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;
 
 
}

方法作用

  • preHandle:在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理;
  • postHandle:在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView (这个博主就基本不怎么用了);
  • afterCompletion:在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);

Spring Boot 整合 Apache Shiro

Apache Shiro™是一个功能强大且易于使用的 Java 安全框架,它执行身份验证、授权、加密和会话管理。借助 Shiro 易于理解的 API,您可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的 Web 和企业应用程序。

https://shiro.apache.org/

官方文档说明:https://shiro.apache.org/spring-boot.html

在开发的时候,发现其登录功能是直接将用户信息存储到Session中, 过于简陋,应该有一个合适的工具来做这件事。

思考过 SpringSecrity 并不是很适合我的口味。故想考虑使用另一款,Shiro。

Shiro三大核心组件

Subject: 即当前用户,在权限管理的应用程序里往往需要知道谁能够操作什么,谁拥有操作该程序的权利,shiro中则需要通过Subject来提供基础的当前用户信息,Subject 不仅仅代表某个用户,与当前应用交互的任何东西都是Subject,如网络爬虫等。所有的Subject都要绑定到SecurityManager上,与Subject的交互实际上是被转换为与SecurityManager的交互。

SecurityManager: 即所有Subject的管理者,这是Shiro框架的核心组件,可以把他看做是一个Shiro框架的全局管理组件,用于调度各种Shiro框架的服务。作用类似于SpringMVC中的DispatcherServlet,用于拦截所有请求并进行处理。

Realm: Realm是用户的信息认证器和用户的权限人证器,我们需要自己来实现Realm来自定义的管理我们自己系统内部的权限规则。SecurityManager要验证用户,需要从Realm中获取用户。可以把Realm看做是数据源

安装依赖
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring-boot-web-starter</artifactId>
  <version>1.9.0</version>
</dependency>
编写配置类
package cn.codeon.config;

import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {
    @Bean
    public Realm realm() {
        SimpleAccountRealm realm = new SimpleAccountRealm();

        realm.addAccount("root", "root");

        return realm;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        chainDefinition.addPathDefinition("/**", "anon"); // all paths are managed via annotations

        // or allow authentication, but NOT require it.
        // chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }
}

除了 SimpleAccountRealm 你也可以自己实现 Realm

直接实现 Realm 接口可能既耗时又容易出错。大多数人选择继承 AuthorizingRealm 抽象类,而不是从头开始。该类实现了常见的身份验证和授权工作流程,以节省您的时间和精力。

内置过滤器