在学习应用css之前我们要先了解一下什么是css。CSS是Cascading Style Sheets(层叠样式表)的简称.
边框(border): css控制的边框属性包括border-width, border-color, border-style.
Border之所以让人很困惑主要源于IE5错综复杂的BUG, 由于IE5是一个“will soon be dead” 的浏览器, 这里只例举一个最为知名的关于border-width的BUG, 让大家更好的理解border的含义, 先看下图:
如上图所示, 对象A(白色矩形)周围有蓝色边框B, 可以看出A的实际宽度为ef, 而IE5不这么认为, 它把cd的长度定义为对象A的宽度, 这个bug在边框的宽度小时几乎察觉不到, 但在边框与对象宽度相差不大时显得尤为明显.
新建一个前端学习qun438905713,在群里大多数都是零基础学习者,大家相互帮助,相互解答,并且还准备很多学习资料,欢迎零基础的小伙伴来一起交流。
现在, 结合以上说明, 可以看出border是独立于对象之外, 位于magin与padding之间(后说明), 具有固定宽度, 颜色和样式的区域.
width:160px;
height:60px;
display:block;
background:url(../image/button.png) no-repeat 0 0;
}
可以看到如 Example2 的效果.
width:160px;
height:60px;
display:block;
background:url(../image/button.png) no-repeat 0 0;
}
在css文件中写入以上代码, 目的在于控制盒子中链接的表现, 通过名为”#button a”的选择器来实现. 链接的宽高为160px*60px, 背景为图片button.png.
在这强调一下display:block的作用. 由于在html文件中,链接<a href=”#”> </a>中没有任何的内容(content)填充, 如果没有声明”display:block”, 那么即使声明了选择器”#button a”的宽高, 浏览器也会因为html文件中没有内容而无法显示链接. 所以”display:block”在这里的作用就在于强制浏览器显示没有内容填充的链接.
用伪类选择器a:link声明链接的背景图片在左上角显示, 即距离左边和顶边分别0, 0. 但由于已经在选择器 “#button a”中声明了图片位置, 此代码可有可无.
用伪类选择器a:hover声明鼠标悬停时, 背景图片上移60px, 而使排在第二位的绿色小图片显示出来;
用伪类选择器a:active声明在鼠标点击与释放之间的状态时, 背景图片上移120px, 而使排在第三位的红色小图片显示;
用伪类选择器a:visited声明在链接被点击或访问过时, 背景图片上移180px, 而使排在第四位的灰色小图片显示;
现在你基本了解了css动态按钮的制作过程, 但以上css代码还存在一个严重的缺陷, 相信你会很快发现问题所在——这个按钮居然是一个”一次性按钮“, 也就是说这个按钮在点击第一次后, 就一直显示那个灰色小图片, 你能想出解决方法吗?
文章目录
一、this
1.什么是this
2.this 代表什么
3.绑定 this 的方法
4.this的指向
5.改变指向
二、Function.prototype.bind()
三、call/apply
1.定义
2.语法
3.异同
一、this
1.什么是this
this 关键字在大部分语言中都是一个重要的存在,JS中自然不例外,其表达的意义丰富多样甚至有些复杂,深刻理解this是学习JS、面向对象编程非常重要的一环。
2.this 代表什么
this代表函数(方法)执行的上下文环境(上下文,类似与你要了解一篇文章,了解文章的上下文你才能清晰的了解各种关系)。
但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。
1.在方法中,this 表示该方法所属的对象。
2.如果单独使用,this 表示全局对象。
3.在函数中,this 表示全局对象。
4.在函数中,在严格模式下,this 是未定义的(undefined)。
5.在事件中,this 表示接收事件的元素。
6.类似 call() 和 apply() 方法可以将 this 引用到任何对象。
3.绑定 this 的方法
this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。
4.this的指向
1.在一般函数方法中使用 this 指代全局对象
function test(){
this.x = 1; //这里this就是window
console.log(this.x);
}
test(); // 1
JS规定,函数中的this,在函数被调用时确定,它指函数当前运行的环境。
2.作为对象方法调用,this 指代上级对象
var x =3;
function test(){
alert(this.x);
}
var o = {
x:1,
m:test
};
o.m(); // 1
如果函数作为对象的方法时,方法中的 this 指向该对象。
3.作为构造函数调用,this 指代new 出的对象
function test(){
console.log(this);
}
var o = new test();
test();
//可以看出o代表的不是全局对象
new关键词的作用是调用某个函数并拿到其中的返回值,只是调用过程稍特殊。在上面的代码实例中。test函数被new关键词调用时,内部依次执行了以下步骤:
(1)创建一个空对象。
(2)将这个空对象的原型,指向这个构造函数的prototype。
(3)将空对象的值赋给函数内部的this(this就是个空对象了)。
(4)执行函数体代码,为this这个对象绑定键值对。
(5)返回this,将其作为new关键词调用oop函数的返回值。
所以构造函数中的this,依旧是在构造函数被new关键词调用时确定其指向,指向的是当前被实例化的那个对象。
4.箭头函数中的this
箭头函数是ES6的新特性,最重要的特点是它会捕获其所在上下文的this作为自己的this,或者说,箭头函数本身并没有this,它会沿用外部环境的this。也就是说,箭头函数内部与其外部的this是保持一致的。
this.a=20
var test={
a:40,
init:()=>{
console.log(this.a)
function go(){
this.a=60
console.log(this.a)
}
go.prototype.a=50
return go
}
}
var p=test.init()
p()
new (test.init())()
//输出 20 60 60 60
5.改变指向
this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。
bind方法和apply、call稍有不同,bind方法返回一个新函数,以后调用了才会执行,但apply、call会立即执行。
二、Function.prototype.bind()
bind()方法主要就是将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,例如:f.bind(obj),实际上可以理解为obj.f(),这时f函数体内的this自然指向的是obj;
示例:
function f(y, z){
return this.x + y + z;
}
var m = f.bind({x : 1}, 2);
console.log(m(3));
//6
1
2
3
4
5
6
这里bind方法会把它的第一个实参绑定给f函数体内的this,所以这里的this即指向{x : 1}对象,从第二个参数起,会依次传递给原始函数,这里的第二个参数2,即是f函数的y参数,最后调用m(3)的时候,这里的3便是最后一个参数z了,所以执行结果为1 + 2 + 3 = 6分步处理参数的过程其实是一个典型的函数柯里化的过程(Curry)。
三、call/apply
1.定义
每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
call和apply可以用来重新定义函数的执行环境,也就是this的指向;call和apply都是为了改变某个函数运行时的context,即上下文而存在的,换句话说,就是为了改变函数体内部this的指向。
2.语法
call()
调用一个对象的方法,用另一个对象替换当前对象,可以继承另外一个对象的属性,它的语法是:
Function.call(obj[, param1[, param2[, [,...paramN]]]]);
1
obj:这个对象将代替Function类里this对象
params:一串参数列表
说明:call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为obj指定的新对象,如果没有提供obj参数,那么Global对象被用于obj。
apply()
和call()方法一样,只是参数列表不同,语法:
Function.apply(obj[, argArray]);
obj:这个对象将代替Function类里this对象
argArray:这个是数组,它将作为参数传给Function
说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个TypeError,如果没有提供argArray和obj任何一个参数,那么Global对象将用作obj。
3.异同
相同点
call()和apply()方法的相同点就是这两个方法的作用是一样的。都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。
一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向,看个例子:
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
console.log(add.call(sub, 2, 1));//3
为什么add.call(sub, 2, 1)的执行结果是3呢,因为call()方法改变了this的指向,使得sub可以调用add的方法,也就是用sub去执行add中的内容,再来看一个例子:
function People(name, age) {
this.name = name;
this.age = age;
}
function Student(name, age, grade) {
People.call(this, name, age);
this.grade = grade;
}
var student = new Student('小明', 21, '大三');
console.log(student.name + student.age + student.grade);//小明21大三
在这个例子中,我们并没有给Student的name和age赋值,但是存在这两个属性的值,这还是要归功于call()方法,它可以改变this的指向。
在这个例子里,People.call(this, name, age);中的this代表的是Student,这也就是之前说的,使得Student可以调用People中的方法,因为People中有this.name = name;等语句,这样就将name和age属性创建到了Student中。
总结一句话就是call()可以让括号里的对象来继承括号外函数的属性。
至于apply()方法作用也和call()方法一样,可以这么写:
People.apply(this, [name, age]);
1
或者这么写:
People.apply(this, arguments);
1
在这里arguments和[name, age]是等价的。
不同点
从定义中也可以看出来,call()和apply()的不同点就是接收参数的方式不同。
1.apply()方法接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
2.call()方法不一定接受两个参数,第一个参数也是函数运行的作用域(this),但是传递给函数的参数必须列举出来。
在给对象参数的情况下,如果参数的形式是数组的时候,比如之前apply()方法示例里面传递了参数arguments,这个参数是数组类型,并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的)就可以采用apply()方法。
但是如果Person的参数列表是这样的(age,name),而Student的参数列表是(name,age,grade),这样就可以用call()方法来实现了,也就是直接指定参数列表对应值的位置Person.call(this,age,name)。
一、什么是字体图标
字体图标:简单的说,就是一种特殊的字体,通过这种字体,显示给用户的就像一个个图片一样,但它的本质是文字。目前在移动端应用比较广泛!
二、字体图标的使用步骤
这里以阿里巴巴矢量图标库为例!!!
sep1:
百度搜索iconfont,找到阿里巴巴矢量图标库官网
网址在这里https://www.iconfont.cn/
进去之后注册或登录,共有3种登录方式,在这里我使用新浪微博账号登录
好了,登录之后我们就可以在里面选择自己想要的字体图标啦!!!那么,如何选择下载并应用到自己的项目中呢??别着急,跟着我走!
sep2:下载字体图标字体库
在这里我们可以根据自己的需求输入相应的关键字进行搜索(中英文都可以)
鼠标放上去,然后就可以把自己喜欢的宝贝加入购物车啦!
网购的赶脚有木有,
我知道,看到这里大家就该有疑问了,
还要花钱买吗?
放心!
答案是:不需要!
我们选的东西都在购物车里啦!
打开购物车,就能看到你选的图标了!!
接下来你要做的是把它们下载到本地。
由于要在网页中使用
在这里我们选择 下载代码
下载后将压缩包解压,为了方便后续使用我们改一下文件夹名称,在这里我改为 icon (注意:在HTML中导入路径时,记得带上你所改的文件夹名称)
打开之后你会发现里面有不同类型的文件(建议都不要删除)
打开后缀名为.html的这个文件(可以更直观地查看自己下载的字体图标)
step3:在项目中引用字体图标
不要走开,重点来了!!!
官方提供了三种引用方法(下面对应的都有步骤)
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.woff2') format('woff2'), 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; -moz-osx-font-smoothing: grayscale; }
第三步:挑选相应图标并获取字体编码,应用于页面
<span class="iconfont">3</span>
font-class 引用
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。
与 Unicode 使用方式相比,具有如下特点:
兼容性良好,支持 IE8+,及所有现代浏览器。
相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
不过因为本质上还是使用的字体,所以多色图标还是不支持的。
使用步骤如下:
第一步:引入项目下面生成的 fontclass 代码:
<link rel="stylesheet" href="./iconfont.css">
第二步:挑选相应图标并获取类名,应用于页面:
<span class="iconfont icon-xxx"></span>
symbol 引用
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
支持多色图标了,不再受单色限制。
通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
兼容性较差,支持 IE9+,及现代浏览器。
浏览器渲染 SVG 的性能一般,还不如 png。
使用步骤如下:
第一步:引入项目下面生成的 symbol 代码:
<script src="./iconfont.js"></script>
第二步:加入通用 CSS 代码(引入一次就行):
<style> .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>
下面,我就跟大家详细说说 font-class 引用的引用方式:
打开编辑器(在这里我使用的是 VS Code编辑器),新建一个项目文件夹(demo)
将解压后的字体图标文件夹(icon)直接放到demo目录下
在demo文件夹下面新建一个html文件 demo.html
导入icon文件夹里面的外部样式表
<link rel="stylesheet" href="./icon/iconfont.css">
iconfont.css 里面就是我们下载的字体图标的所有css样式了,我们打开看看吧!
你会发现里面有一个 iconfont类名(这里划重点!后面要用),它是所有字体图标的公用样式。
这时我们就可以在页面中使用这些字体图标啦!上面我们只是导入了整个css样式表,现在我们要针对性地把某个图标引用到html页面中,并在网页中显示出来。
下面 我们就开始写页面的主体部分吧!
<p> 我有一个梦想:能够拥有一套 <span class="iconfont icon-home"></span> </p>
这里的span标签里面放的就是要用的某个字体图标了,你会发现它用了两个class类名。
第一个是iconfont,也就是我前面划的重点(到这里不明白的话,可以再回头看看)
第二个是icon-home,这个类名从何而来呢?
来,继续往下看,再次打开icon文件夹下的
话不多说,直接看图
是不是对应上啦!简单地说就是你要用那个图标就添加它的class类名。
到这里,恭喜你已经学会了如何在网页中插入字体图标啦!!!
那么,赶快运行一下看看效果吧!
到这里是不是还满足不了你的需求,会有这样的疑问:如果是这样的效果,跟一张图片有什么区别呢???
当然不是啦!
之所以叫做字体图标,顾名思义,它在网页中就是一种字体的存在,不过它比普通字体长得好看些有木有!
我们可以像更改文字样式一样去更改它的样式,比如说 大小、颜色、阴影…
Symbol 引用方式还可以实现下面原图标的彩色效果哦!可以自己照着官方提供的步骤试试看
下面附上Font class引用方法的 源代码 供参考
前面的步骤一定要看!!!
如果前期工作没做的话,直接用我的源代码是实现不了的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>字体图标</title>
<link rel="stylesheet" href="./icon/iconfont.css">
<style>
.iconfont{
font-size: 200px;
color: palevioletred;
text-shadow: 18px 17px 17px gray;
}
</style>
</head>
<body>
<p>
我有一个梦想:能够拥有一套
<span class="iconfont icon-home"></span>
</p>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
花费了很长时间整理出来的。
很简单是不是!!!
看到这里还不会的话,建议重新再看一遍!
其中有错误的话,还请指出,我会虚心接受并改正!!!
————————————————
版权声明:本文为CSDN博主「Humy.」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42678796/article/details/104569773
一、建立一个库
1、git clone [url] // 克隆代码
2、设置贡献者
git config --global user.name "" // 设置当前本地库username
git config --global user.email "" // 设置当前本地库useremail
git config --global user.email // 查看当前本地库useremail
git config --list // 查看所以配置项
二、git的三个区
1、工作区:本地编写代码的地方叫工作区
2、暂存区:工作区改好的代码先提交到暂存区,然后由暂存区将代码提交到版本库
- 作为过渡层
- 避免误操作
- 保护工作区和版本区
- 分支处理
JavaScript基础知识——JS预解析
js代码是由浏览器中的JavaScript解析器来执行的。JavaScript解析器在运行JavaScript代码时分为两步:1预解析、2代码执行。
预解析
预解析是指js引擎会把js里面所有的var与function提升到当前作用域的最前面。(这里的当前作用域包括:全局作用域与局部作用域)。
预解析可分为:变量预解析和函数预解析
变量预解析:就是把所有的变量声明提升到当前的作用域的最前面但是不提升赋值操作。如下例所示:
<script>
console.log(num); //结果为undefined
var num = 10;
</script>
//其实际执行过程为
var num;
console.log(num);
num=10;
函数预解析:就是把所有的函数声明提升到当期作用域的最前面 但是不包括调用函数。如下例所示:
var num = 10
fun();
function fun() { //结果是undefined
console.log(num);
var num = 20;
}
//其实际执行过程为
var num;
funtion fun() {
var num;
console.log(num);
num=20;
}
num = 10;
fun();
在idea中使用jdbc往数据库里储存中文乱码问题
这里使用的数据库是mysql。
ide为idea.
有时做一些web项目时需要往数据库里面储存中文,就是需要用到jdbc往数据库里面储存数据时,参数改为中文。可是储存完之后,打开sqlyog查询又是???这样子的乱码
上网找了很多方法,数据库的编码问题都改了,而且统一成utf-8了,但还是储存时为乱码。
后面检查时在sqlyog里改中文又可以正常显示。
这就说明数据库上是没有问题的,应该是连接这块,于是在连接的url上加入了参数就可以正常显示了characterEncoding=UTF-8
这里使用的是c3p0的连接池,不同的连接池可以去对应的配置文件中加上参数
在Vue中,用watch来响应数据的变化,示例代码如下,
第一种方式
<input type="text" v-model="userName"/>
//监听当userName值发生变化时触发
watch: {
userName (newName, oldName) {
console.log(newName)
}
}
第一种方式有一个缺点: 就是当值第一次绑定的时候 不会执行监听函数,只有当值改变的时候才会执行。
如果我们想在第一次绑定的时候执行此监听函数,则需要设置immediate为true。比如当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也需要执行函数,此时就需要将immediate设为true。
第二种方式
watch: {
userName: {
handler (newName, oldName) {
console.log(newName)
},
immediate: true
}
}
immediate表示在watch中首次绑定的时候,是否执行handler,值为true则表示在watch中声明的时候,就立即执行handler方法,值为false,则和一般使用watch一样,在数据发生变化的时候才执行handler。
当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听。
第三种方式
<input type="text" v-model="cityName.name" />
data (){
return {
cityName:
{
name:'北京',
location: '中国'
}
}
},
watch: {
cityName: {
handler(newName, oldName) {
console.log(newName)
},
immediate: true,
deep: true
}
}
注:监测为对象的时候,newVal == oldVal
此时会给cityName的所有属性都加上监听函数,如果属性较多时,每个属性值的变化都会执行handler。如果只需要监听对象中的一个属性值,则可以做以下优化:使用字符串的形式监听对象属性:
watch: {
'cityName.name': {
handler(newName, oldName) {
console.log(newName)
},
immediate: true,
deep: true
}
}
数组的变化不需要深度监听;
在watch中不要使用箭头函数,因为箭头函数中的this是指向当前作用域.
首先让我们了解一下前端路由:路由router全部配置在前端,根据用户权限判断可以进入哪些页面
缺点:
vue初始化的时候需要挂载全部路由,对性能有影响
安全性低,用户可以在地址栏跳转到无权访问的页面(可优化)
动态路由则是根据用户信息获取权限,简单来说就是根据用户信息获取其对应的权限,生成对应的路由挂载,然后动态渲染有权限的菜单于侧边栏
实现
定义静态路由(登录或者公用页面)、动态路由,vue初始化时只挂载静态路由
用户登录后,拿到用户token,调接口拿到动态路由权限DynamicRoutes,将DynamicRoutes和定义的动态路由比较,筛选出相应的用户可访问路由表
执行router.addRoutes(DynamicRoutes)添加动态路由
使用vuex存储路由表,根据vuex中可访问的路由渲染侧边栏sidebar
// beforeEach中
if (getToken() && getToken() !== 'undefined') {
// 权限判断
if (!store.state.app.menuPermissions) {
/ 获取后台给的权限数组 /
return new Promise((resolve, reject) => {
getPermissionList().then(response => {
if (response.data.stat === 1) {
const userRouter = response.data.data
// 检查并生成新的路由表
const DynamicRoutes = ChecAndSetPermissionRouter(userRouter)
// 默认使/重定向到第一个有效的路由
for (let i = 0, leni = DynamicRoutes.length; i < leni; i++) {
if (DynamicRoutes[i].children.length > 0) {
DynamicRoutes[i].path = '/'
DynamicRoutes[i].redirect = DynamicRoutes[i].children[0].path
break
}
}
DynamicRoutes.push({ path: '', redirect: '/404', hidden: true }) // 全局404
/ 生成左侧导航菜单 /
store.dispatch('SetMenuPermissions', DynamicRoutes)
/ 动态添加路由 /
router.addRoutes(DynamicRoutes)
// / 完整的路由表 /
store.dispatch('SetRouterPemissions', [...constantRouterMap, ...DynamicRoutes])
next(to)
}
}).catch(error => {
router.push('/404')
// / 生成左侧导航菜单 */
store.dispatch('SetMenuPermissions', [])
next()
reject(error)
})
})
}
if (to.path === '/login') {
next({ path: '/' })
} else {
next()
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next(/login?redirect=${to.path}
) // 否则全部重定向到登录页
}
}
踩坑来了
Q:为什么404 页面一定要最后加载,放置在静态路由中会怎么样?
放在静态路由里,后面的所以页面都会被拦截到404,所以应该获取动态路由权限之后push
Q:权限获取成功,不跳转新生成的动态路由,跳404?
beforeEach中router.addRoutes之后的next()可能会失效,因为可能next()的时候路由并没有完全add完成,可替换成next(to),重新进入router.beforeEach这个钩子,这时候再通过next()来释放钩子,就能确保所有的路由都已经挂在完成了。
Q:$router.addRoutes()动态添加的路由怎么删除掉?
在开发中,有新增编辑删除菜单并要求左侧边栏菜单及时更新的需求,如果直接addRoutes,warn如下:
解决:addRoutes之前要清除掉上次addRoutes的路由,所以操作菜单调取权限后重新初始化router,进行matcher赋值
// DynamicRoutes是权限路由
const createRouter = () => new Router({
mode: 'hash',
routes: []
})
const newRouter = createRouter()
// resetRouter()
this.$router.matcher = newRouter.matcher
this.$router.addRoutes(DynamicRoutes)
Q:莫名其妙的无限循环
vue-admin-template,遇到二级菜单children为空的权限,报错如下:
解决:按照github-issues上方法,在SidebarItem.vue里改一下data就好了(没想通为啥)
// 更改后如下,return {}
data() {
this.onlyOneChild = null
return {}
}
附:ChecAndSetPermissionRouter
import { dynamicRouterMap } from '@/router'
export function ChecAndSetPermissionRouter(permissionDatas) {
// 获取到权限hashmap
var permissionHashMap = null
permissionHashMap = GetPermissionHashMap(permissionDatas)
// 标记路由表
var newDynamicRouterMap = []
newDynamicRouterMap = objDeepCopy(dynamicRouterMap)
newDynamicRouterMap.forEach(item => {
MarkRouter(null, item, permissionHashMap)
})
// 重设路由表
for (let i = 0; i < newDynamicRouterMap.length; i++) {
if (ResetRouter(newDynamicRouterMap, newDynamicRouterMap[i])) {
i-- // 注意:防止移除后索引错位
}
}
return newDynamicRouterMap
}
function GetPermissionHashMap(permissionDatas) {
var permissionHashMap = {}
permissionDatas.forEach(item => {
SetKeyValueOfNodes(null, item, permissionHashMap)
})
return Object.assign({}, permissionHashMap)
}
// 深拷贝,递归重新设置前端路由表,避免数据复用
function objDeepCopy(source) {
var sourceCopy = source instanceof Array ? [] : {}
for (var item in source) {
sourceCopy[item] = typeof source[item] === 'object' ? objDeepCopy(source[item]) : source[item]
}
return sourceCopy
}
// 为权限hashmap的属性赋值,新增属性tempKey/tempKey2
function SetKeyValueOfNodes(p, c, permissionHashMap) {
// 需要匹配的组合类型
var tempKey = (p ? p.name : 0) + '' + c.name
var tempKey2 = c.name + '' + c.name
// 赋值
permissionHashMap[tempKey] = 1
permissionHashMap[tempKey2] = 1
// 递归遍历子节点赋值
if (c.children != null && c.children.length > 0) {
c.children.forEach(item => {
SetKeyValueOfNodes(c, item, permissionHashMap)
})
}
}
// 标记路由表
function MarkRouter(p, c, permissionHashMap) {
var key = (p ? p.meta.title : 0) + '_' + c.meta.title
// 使用拼接的key作为参考标记去匹配有权限的路由表
if (HasPermission(key, permissionHashMap)) {
if (p != null) {
p.keep = true // 保留当前节点
}
if (c != null) {
c.keep = true
}
}
if (c.children && c.children.length > 0) {
c.children.forEach(item => {
MarkRouter(c, item, permissionHashMap)
})
}
}
// 校验后端接口是否存在当前节点
function HasPermission(key, permissionHashMap) {
return permissionHashMap[key] === 1
}
// 重置路由表
function ResetRouter(p, c) {
if (c == null) {
return false
}
if (p.children && !c.keep) {
p.children.splice(p.children.indexOf(c), 1)
return true
} else if (!c.keep) {
p.splice(p.indexOf(c), 1)
return true
}
if (c.children && c.children.length > 0) {
for (let i = 0; i < c.children.length; i++) {
if (ResetRouter(c, c.children[i])) {
i-- // 注意:防止移除后索引错位
}
}
}
return false
}
禁用继承属性inheritAttrs和$attrs的使用
Index.html:
<div id="app">
<test-input
v-bind:class="class1"
v-bind:style="{fontSize:17+'px'}"
v-bind:test1='test1'
test2="test2"
placeholder="placeholder test3"
></test-input>
</div>
Index.js:
Vue.component('test-input', {
inheritAttrs: false,
template: <label ><br /> <p v-bind="$attrs">测试</p><br /> <input v-bind="$attrs"><br /> </label>
})
new Vue({
el:'#app',
data:{
class1:'class1note',
test1:'test1note',
test2:'test2note',
},
})
页面结果:
当将属性修改为:inheritAttrs: true,的时候
页面的结果:
包含了所有的属性:
最后总结:
当inheritAttrs的属性值为true(不写该行属性的结果同true,也就是inheritAttrs默认为true),组件的根元素会自动继承所有的属性!当为false的时候,根元素只会继承注册的属性,自建的属性不会继承!!
attrs:它包含了父组件所有的自建属性!可以通过v−kind=“ attrs:它包含了父组件所有的自建属性!可以通过v-kind=“attrs:它包含了父组件所有的自建属性!可以通过v−kind=“attrs”将所有自建属性赋给想要的元素上!!
蓝蓝设计的小编 http://www.lanlanwork.com