在學習應用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)。
一、什么是字體圖標
字體圖標:簡單的說,就是一種特殊的字體,通過這種字體,顯示給用戶的就像一個個圖片一樣,但它的本質是文字。目前在移動端應用比較廣泛!
二、字體圖標的使用步驟
這里以阿里巴巴矢量圖標庫為例?。。?br />
sep1:
百度搜索iconfont,找到阿里巴巴矢量圖標庫官網
網址在這里https://www.iconfont.cn/
進去之后注冊或登錄,共有3種登錄方式,在這里我使用新浪微博賬號登錄
好了,登錄之后我們就可以在里面選擇自己想要的字體圖標啦?。?!那么,如何選擇下載并應用到自己的項目中呢??別著急,跟著我走!
sep2:下載字體圖標字體庫
在這里我們可以根據自己的需求輸入相應的關鍵字進行搜索(中英文都可以)
鼠標放上去,然后就可以把自己喜歡的寶貝加入購物車啦!
網購的趕腳有木有,
我知道,看到這里大家就該有疑問了,
還要花錢買嗎?
放心!
答案是:不需要!
我們選的東西都在購物車里啦!
打開購物車,就能看到你選的圖標了?。?br />
接下來你要做的是把它們下載到本地。
由于要在網頁中使用
在這里我們選擇 下載代碼
下載后將壓縮包解壓,為了方便后續使用我們改一下文件夾名稱,在這里我改為 icon (注意:在HTML中導入路徑時,記得帶上你所改的文件夾名稱)
打開之后你會發現里面有不同類型的文件(建議都不要刪除)
打開后綴名為.html的這個文件(可以更直觀地查看自己下載的字體圖標)
step3:在項目中引用字體圖標
不要走開,重點來了!?。?br />
官方提供了三種引用方法(下面對應的都有步驟)
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
花費了很長時間整理出來的。
很簡單是不是!?。?br />
看到這里還不會的話,建議重新再看一遍!
其中有錯誤的話,還請指出,我會虛心接受并改正?。?!
————————————————
版權聲明:本文為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.syprn.cn