<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
</head>
<div id="app">
<!--簡單表達式 [類型一樣直接加]=25-->
<h1>{{5+5}}</h1>
<!-- +:運算,字符串連接 【類型不一樣就是拼接】=5v5,55-->
<h1>{{5+"v5"}}</h1>
<h1>{{5+"5"}}</h1>
<!-- -:減法 "5"-"5" 兩個雙引號 自動解析【類型一樣直接算】=0,25-->
<h1>{{"5"-"5"}}</h1>
<h1>{{5*5}}</h1>
<!-- *:乘 【一樣類型一樣直接乘】=25-->
<h1>{{"5"*"5"}}</h1>
<!-- / 除 【不說了一樣】=1,1-->
<h1>{{5/5}}</h1>
<h1>{{"5"/"5"}}</h1>
</div>
</body>
<script>
var app=new Vue({
el:"#app"//掛載到id
});
</script>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<body>
<div class="app">
{{show?"GG":"MM"}}
</div>
</body>
<script>
var app=new Vue({
el:".app",
data:{
show:true//true就是MM,false就是GG
}
});
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
{{message}}<br>
<!--長度-->
{{message.length}}<br>
<!--截取根據下標-->
{{message.substring(0,3)}}
<!--根據下標從哪里開始 【3456】-->
{{message.substring(2).toUpperCase()}}<br>
<!--獲取到下標 【3】-->
{{message.charAt(2)}}
</div>
</body>
<script>
var app=new Vue({
el:"#app",
data:{
message:"123456"
}
});
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!--123456-->
{{message}}<br>
<!--{ "name": "華雄", "age": 69 }重寫toString,就變了-->
{{user}}<br>
<!--華雄-->
{{user.name}}<br>
<!--getName(){return this.name}-->
{{user.getName}}<br>
<!--toString(){return this.name}-->
{{user.toString}}<br>
<!--{"name":"華雄","age":69} -->
{{JSON.stringify(user)}}
<!--22 json轉成字符串了-->
{{JSON.stringify(user).length}}
</div>
</body>
<script>
var sss={
name:"華雄",
age:69,
getName(){return this.name},//{ "name": "華雄", "age": 69 }
//原toString---function toString() { [native code] }
toString(){return this.name}//重寫toString,這樣獲取到就是華雄
}
var app=new Vue({
el:"#app",
data:{
message:"123456",
user:sss
}
});
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
{{woman}}<br>
{{woman[0]}}<br>
{{woman.length}}<br>
{{woman.toString()}}<br>
{{woman.join(" + ")}}
</div>
</body>
<script>
var app=new Vue({
el:"#app",
data:{
woman:["黃月英","蔡文姬","孫尚香","甄宓"]
}
});
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!--純文本 是什么樣就展示什么-->
<span v-text="msg"></span><br>
<!--解析標簽 會自動解析標簽-->
<span v-html="msg"></span>
</div>
</body>
<script>
new Vue({
el:"#app",
data:{
msg:"<h3>你好!中國</h3>"
}
})
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!--就是直接循環-->
<ul>
<li v-for="a in woman">{{a}}</li>
</ul>
<!--循環a和下標index-->
<ul>
<li v-for="(a,index) in woman">{{a}}---{{index}}</li>
</ul>
<!--搞一個表-->
<table border="1px black">
<!--表頭-->
<tr>
<th>名字</th>
<th>年齡</th>
</tr>
<!--循環里面的東西-->
<tr v-for="key in users">
<!--
aa in key aa:value值
aa,bb in key aa:value值 bb:屬性名
aa,bb,index,index aa:value值 bb:屬性名 index:下標
-->
<td v-for="(aa,bb,index) in key">
{{aa}}----{{bb}}---{{index+1}}
</td>
</tr>
</table>
</div>
</body>
<script>
new Vue({
el:"#app",
data:{
woman:["黃月英","蔡文姬","孫尚香","甄宓"],
<!--List<user>-->
users:[{
name:"張三",
age:10
},{
name:"李四",
age:20
}]
}
})
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<!--原版以前這么寫-->
<img src="123456.JPG" title="">
<!--新版 可以實現綁定,這樣就能寫活了-->
<img v-bind:src="src" v-bind:title="sss">
<!--title就是鼠標提示-->
<img :src="src" v-bind:title="sss">
</div>
</body>
<script>
new Vue({
el:"#app",
data:{
//下面的值現在是寫死,以后從后臺獲取。
src:"123456.JPG",
sss:"手放哪呢?"
}
});
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
</head>
<!--這個都是一些雙向綁定的案例,不好解釋,太麻煩了!需要的時候代碼考過去自己一看就明白了了-->
<body>
<div id="app">
<h3>綁定到type=text的input表單元素</h3>
姓名:<input type="text" v-model="inputValue"><br/>
data中的值:{{inputValue}}
<h3>綁定到type=checkbox的input表單元素</h3>
<!--v-model="checkboxValue" checkboxValue數組包含了當前value值 就會默認選中-->
打籃球:<input type="checkbox" v-model="checkboxValue" value="打籃球"><br/>
踢足球:<input type="checkbox" v-model="checkboxValue" value="踢足球"><br/>
data中的值:{{checkboxValue}}
<h3>綁定到type=radio的input表單元素</h3>
男:<input type="radio" v-model="radioValue" value="男"><br/>
女:<input type="radio" v-model="radioValue" value="女"><br/>
data中的值:{{radioValue}}
<h3>綁定到textarea的元素</h3>
個人簡介:<textarea v-model="textareaValue"></textarea><br/>
data中的值:{{textareaValue}}
<h3>綁定到單選的select的元素</h3>
技能:<select v-model="skills">
<option value="java">java</option>
<option value="php">php</option>
<option value=".net">.net</option>
</select><br/>
data中的值:{{skills}}
</div>
</body>
<script>
var vue=new Vue({
el:"#app",
data:{
inputValue:"輸入框的值",
checkboxValue:["打籃球"],
radioValue:"女",
textareaValue:"文本域的值",
skills:"php"
}
});
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<span v-show="show">顯示</span><br>
<span v-show="hidden">不顯示</span><br>
<span v-show="score<60">小于60分顯示</span><br>
<span v-show="score>60">大于60分顯示</span>
</div>
</body>
<script>
var app=new Vue({
el:"#app",
data:{
show:true,
hidden:false,
score:59
}
});
</script>
</html>
內容是《Web前端開發之Javascript視頻》的課件,請配合大師哥《Javascript》視頻課程學習。
為了便于操作基本類型值,ECMAScript還提供了3個特殊的引用類型:Boolean、Number和String;這些類型與以上所說的引用類型相似,但同時也具有與各自的基本類型相應的特殊行為;實際上,每當讀取一個基本類型值的時候,后臺就會創建一個對應的基本包裝類型的對象,從而讓我們能夠用一些方法來操作這些數據,如:
var s1="zero network";
var s2=s1.substring(4);
alert(s2);
基本數據類型不是對象,所以從邏輯上它們不應該有方法(實際上有)(原理:為了能夠實現這種直觀的操作,后臺已經自動完成了一系列的處理:創建String對象實例,同樣適用于Boolean和Number類型對應的布爾值和數字值。
基本數據類型與基本包裝類型有本質上的不同,產生從類型的角度來講,它們就是不同的;其保存的位置也不同。
引用類型與基本包裝類型的區別就是對象的生存期:使用new操作符創建的引用類型對象,在執行流離開當前作用域之前都一直保存在內存中,而自動創建的基本包裝類型的對象,則只存在于一行代碼的執行瞬間,然后立即被銷毀;這意味著不能在運行時為基本類型值添加屬性和方法,如:
var s1="zero network";
s1.color="red";
alert(s1.color); // undefined
當然可以使用Boolean, Number和String顯示的來創建基本包裝類型的對象,但絕大部分情況下不需要,除非在絕對必要的情況下,因為這種做法很容易讓開發者分不清是在處理基本類型還是引用類型的值;
對基本包裝類型的實例調用typeof會返回object,而且所有基本包裝類型的對象在轉換為布爾類型時值都是true;如:
var s1=new Number(0);
// var s1=0;
alert(typeof s1);
alert(Boolean(s1));
Object構造函數,僅接受一個參數,其會根據傳入值的類型返回相應基本包裝類型的實例,如:
var obj=new Object("zeronetwork");
alert(obj instanceof String); // true
var n=new Object(5);
alert(n instanceof Number); // true
console.log(n.constructor);
說明:傳遞的值是動態的,直到運行時才確定其類型,Object()可能會調用另一個內置函數來創建對象,并且返回了一個以不同構造函數所創建的對象。
ES在必要的時會將包裝對象轉換成原始值,但不總是這樣;如:
var s1="zeronetwork";
var s2=new String("zeronetwork");
console.log(s1==s2); // true
console.log(s1===s2); // false
var n1=18;
var n2=new Number(18);
console.log(n1==n2); // true
console.log(n1===n2); // false
說明:“==”運算符會將原始值與包裝對象視為相等,但“===”全等會將它們視為不等,因為它們的類型不同,可以使用typeof查看。
注意:使用new調用基本包裝類型的構造函數,與直接調用同名的轉型函數是不一樣的,如:
var value="25";
var number=Number(value); // 轉型函數
alert(typeof number); // number
var obj=new Number(value); // 構造函數
alert(typeof obj); // object
注:null與undefined沒有包裝對象,訪問它們的屬性會造成一個類型錯誤。
盡管在實際場景中不太使用基本包裝類型,并且也不建議使用,但它們操作基本類型值的能力還是相當重要的,而每個基本包裝類型都提供了操作相應值的便捷方法。
Boolean類型:
Boolean類型是與布爾值對應的引用類型,Boolean 對象表示兩個值:"true" 或 "false"。
創建Boolean對象:var oBool=new Boolean(true); // 傳入true或false值
注:如果邏輯對象無初始值或者其值為 0、-0、null、""、false、undefined 或者 NaN,那么對象的值為 false,否則,其值為 true;
在實際場景中,Boolean對象的用處不大,因為經常會造成一些誤解,其中最常見的問題就是在布爾表達式中使用Boolean對象,如:
var bObj=new Boolean(false);
alert(bObj && true); // true
var bValue=false;
alert(bValue && true); // false
基本類型與引用類型還有兩個區別,首先,typeof操作符對基本類型返回boolean,而對引用類型返回object;其次,由于Boolean類型的實例,所以使用instanceof操作符測試Boolean對象會返回true,而測試基本類型的布爾值則返回false,如:
var falseObject=new Boolean(false);
var falseValue=false;
alert(typeof falseObject); // object
alert(typeof falseValue); // boolean
alert(falseObject instanceof Boolean); // true
alert(falseValue instanceof Boolean); // false
總結:理解基本類型的布爾值與Boolean對象之間的區別非常重要,建議不要使用,最好使用Boolean原始值;
可以使用Boolean(參數)進行數據類型轉換;
Number對象:
Number 對象,是原始數值的包裝對象。在必要時,JavaScript 會自動地在原始數據和對象之間轉換;
可以用構造函數 Number() 明確地創建一個 Number 對象:
var numberObject=new Number(10);
Number對象屬性:
console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);
var x=-Number.MAX_VALUE * 2;
if(x==Number.NEGATIVE_INFINITY)
console.log("X的值:" + x);
var y=Number.MAX_VALUE * 2;
if(y==Number.POSITIVE_INFINITY)
console.log("Y的值:" + y);
注: Number.NaN 是一個特殊值,說明某些算術運算(如求負數的平方根)的結果不是數字。方法 parseInt() 和 parseFloat() 在不能解析指定的字符串時就返回這個值。
Number類或對象方法:
var number=new Number(1337);
console.log(number.toString(8));
var num=new Number(12.345678);
console.log(num.toFixed(2)); // 12.35
var num=1200.00;
console.log(num.toExponential(2));
var num=99;
console.log(num.toPrecision(1)); // 1e+2
console.log(num.toPrecision(2)); // 99
console.log(num.toPrecision(3)); // 99.0
num=10000;
console.log(num.toPrecision(1)); // 1e+4
console.log(num.toPrecision(4)); // 1.000e+4
注意:與Boolean對象類似,Number對象也以后臺方式為數值提供了重要的功能;不建議直接實例化Number類型,原因與Boolean一樣;一般情況下是使用數字的原始表示法;
var numberObject=new Number(10);
var numberValue=10;
alert(typeof numberObject); // object
alert(typeof numberValue); // number
alert(numberObject instanceof Number); // true
alert(numberValue instanceof Number); // false
String對象:
String類型是字符串對象包裝類型,用于處理文本(字符串)。語法:var str=new String(str); 如:
var oStr=new String();
var oStr=new String("零點程序員");
var oStr=String("525");
length屬性:返回字符串中的字符個數 如: str.length ;
注:即使字符串包含雙字節字符(不是占一個字節的ASCII字符),每個字符也仍然算一個字符;
String 類定義了大量操作字符串的方法,例如從字符串中提取字符或子串,或者檢索字符或子串。
需要注意的是,JavaScript 的字符串是不可變的(immutable),String 類定義的方法都不能改變字符串的內容。像 String.toUpperCase() 這樣的方法,返回的是全新的字符串,而不是修改原始字符串。
1)字符方法:
用于訪問字符串中特定字符的方法:charAt()和charCodeAt();這兩個方法都接收一個參數,即基于0的字符位置;其中,charAt()方法以單字符字符串的形式返回給定位置的那個字符(ECMAScript中沒有字符類型),如:
var str="zero network";
alert(str.charAt(2));
如果想得到字符編碼而不是字符時,使用charCodeAt(),如:
var str="zero network";
alert(str.charCodeAt(2)); // 114
ECMAScript還定義了另一個訪問字符的方法,即使用括號(類似于訪問數組元素)語法,使用數字索引來訪問字符串中的特定字符,如:
var str="zero network";
alert(str[2]); // 2
2)字符串操作方法:
concat()方法:用于將一或多個字符串拼接起來,返回拼接得到的新字符串,如:
var str = "zero";
var result = str.concat("network");
alert(str);
alert(result);
concat()方法可以接受多個任意參數,即可以通過它可以拼接任意多個字符串,如:
var str = "zero";
var result = str.concat("network","!");
alert(result);
說明:雖然concat()是專門用來拼接字符串的,但在實際場景中使用更多的還是加號操作符(+);而且,使用加號操作符在大多數情況下都比使用concat方法要簡便易行,特別是在拼接多個字符串的情況下;
ECMAScript提供了三個基于字符串創建新字符串的方法:slice()、substr()和substring();這三個方法都會返回被操作字符串的一個子字符串,而且也都接受一或兩個參數;第一個參數指定子字符串的開始位置,第二個參數表示子字符串到哪里結束;具體來說,slice()和substring()的第二個參數指定的是子字符串最后一個字符后面的位置;而substr()的第二個參數指定的則是返回的字符個數;如果沒有給這些方法傳遞第二個參數,則將字符串的末尾作為結束位置,如:
var str = "zero network";
alert(str.slice(3));
alert(str.substring(3));
alert(str.substr(3));
alert(str.slice(3,6));
alert(str.substring(3,6));
alert(str.substr(3,6));
同時這些方法的值可以是負值;其中,slice()會將傳入的負值與字符串的長度相加,substr()將負的第一個參數加上字符串的長度,而將負數的第二個參數轉換為0,substring()方法會把所有負值參數都轉換為0,如:
var str = "zero network";
alert(str.slice(-3));
alert(str.substring(-3));
alert(str.substr(-3));
alert(str.slice(3,-4));
alert(str.substring(3,-4));
alert(str.substr(3,-4));
一個小示例:
<style>
#mydiv{width: 100px; height: 1.5em; border: 1px solid; overflow: hidden;}
</style>
<div id="mydiv"></div>
<script>
var msg="北京零點網絡科技有限公司";
var spacer="...";
var pos=0;
var mydiv = document.getElementById("mydiv");
function ScrollMsg(){
mydiv.innerHTML = msg.substring(pos,msg.length) + spacer + msg.substring(0, pos);
pos++;
if(pos>msg.length) pos=0;
window.setTimeout("ScrollMsg()",200);
}
ScrollMsg();
</script>
3)字符串位置方法:
有兩個可以從字符串中查找子字符串的方法:indexOf()和lastIndexOf();這兩個方法都是從一個字符串中搜索給定的子字符串,然后返回子字符串的位置,如果沒有找到子字符串,則返回-1;兩個方法的區別在于,一個從開頭向后搜索子字符串,而lastIndexOf是從末尾向前搜索,如:
var str = "zero network";
alert(str.indexOf("o"));
alert(str.lastIndexOf("o"));
說明:如果o在字符串只出現了一次,則兩個方法會返回相同的位置值;
這兩個方法都可以接受可選的第二個參數,表示從字符串中的哪個位置開始搜索;如:
var str = "zero network";
alert(str.indexOf("o",6));
alert(str.lastIndexOf("o",6));
在使用第二個參數的情況下,可以循環調用這兩個方法來找到所有匹配的子字符串,如:
var str = "lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();
var pos = str.indexOf("e");
while(pos>-1){
positions.push(pos);
pos = str.indexOf("e",pos + 1);
}
alert(positions); // 3,24,32,35,52
4)trim()方法:
ECMAScript5為所有字符串定義了trim()方法;該方法會創建一個字符串的副本,刪除前置及后綴的所有空格,然后返回結果,如:
var str = " zero network ";
var trimStr = str.trim();
alert(str);
alert(trimStr);
此外,還有兩個非標準的trimLeft()和trimRight()方法,分別用于刪除字符串開頭和末尾的空格;
var trimStr = str.trimLeft();
var trimStr = str.trimRight();
5)字符串大小寫轉換方法:
涉及字符串大小寫轉換的方法有4個:
其中toLowerCase()和toUpperCase()是最常用的方法;而toLocaleLowerCase()和toLocaleUpperCase()則是針對特定地區的實現,對有些地區來說,針對地區的方法與其通用方法得到的結果相同,但少數語言(如土耳其語)會為Unicode大小寫轉換應用特殊的規則,這時候就必須使用針對地區的方法來保證實現正確的轉換,如:
var str = "ZERO network";
alert(str.toLocaleLowerCase());
alert(str.toLocaleUpperCase());
alert(str.toLowerCase());
alert(str.toUpperCase());
6)字符串的模式匹配方法:
String類型定義了幾個用于在字符串中匹配模式的方法;
match()方法:本質上與RegExp的exec()方法相同;其只接受一個參數,要么是一個正則表達式,要么是一個RegExp對象,如:
var text = "cat, bat, sat, fat";
var pattern = /.at/;
var matches = text.match(pattern);
alert(matches);
alert(matches.index);
alert(matches[0]);
alert(pattern.lastIndex);
說明:match()返回了一個數組,其第一項是與整個模式匹配的字符串,之后的每一項(如果有)保存著與正則表達式中的捕獲組匹配的字符串;
search()方法:接受的參數與match()一樣;該方法返回字符串中第一個匹配項的索引;如果沒有找到匹配項,則返回-1;如:
var text = "cat, bat, sat, fat";
var pos = text.search(/at/);
alert(pos); // 1
replace()方法:此方法的目的是為了簡化替換子字符串的操作;該方法接受兩個參數,第一個參數可以是一個RegExp對象或一個字符串(這個字符串不會被轉換成正則表達式),第二個參數可以是一個字符串或一個函數;如果第一個參數是字符串,那么只會替換第一個子字符串,要想替換所有子字符串,唯一的辦法就是提供一個正則表達式,而且要指定全局g標志,如:
var text = "cat, bat, sat, fat";
var result = text.replace("at","ond");
alert(result); // cond, bat, sat, fat
result = text.replace(/at/g,"ond");
alert(result); // cond, bond, sond, fond
split()方法:
可以基于指定的分隔符將一個字符串分割成多個子字符串,并將結果放在一個數組中;分隔符可以是字符串,也可以是RegExp對象;其可以接受可選的第二個參數,用于指定數組的大小,以便確保返回的數組不會超過既定大小,如:
var colorText = "red,blue,green,yellow";
var colors1 = colorText.split(",");
var colors2 = colorText.split(",",2);
var colors3 = colorText.split(/[^\,]+/);
alert(colors1); // red,blue,green,yellow
alert(colors2); // red,blue
alert(colors3); // ,,,,,,,
7)localeCompare()方法:
localeCompare()用本地特定的順序來比較兩個字符串,默認返回下列值中的一個:-1、0、1,如:
var str = "yellow";
alert(str.localeCompare("brick")); // 1
alert(str.localeCompare("yellow")); // 0
alert(str.localeCompare("zoo")); // -1
注:利用localeCompare()可以自定義返回的值,如:
function determineOrder(value){
var result = stringValue.localeCompare(value);
if(result < 0){
alert("yellow在'"+value+"'之前");
}else if(result > 0){
alert("yellow在'"+value+"'之后");
}else{
alert("yellow與'"+value+"'相等");
}
}
determineOrder("brick");
determineOrder("yellow");
determineOrder("zoo");
localeCompare()方法比較與眾不同的地方,就是實現所支持的地區(國家和語言)決定了這個方法的行為;如,美國以英語作為ECMAScript實現的標準語言,因此localeCompare()就是區分大小寫的,于是大寫字母在字母表中排在小寫字母前頭就成為了一項決定性的比較規則;在其他地區有可能就不是這種情況了。
8)fromCharCode()方法:
String構造函數本身還有一個靜態方法:fromCharCode(),這個方法的任務是接受一或多個字符編碼,然后將它們轉換成一個字符串;從本質上看,這個方法與實例方法charCodeAt()執行的是相反的操作,如:
alert(String.fromCharCode(104,101,108,108,111)); // hello
9)HTML方法:
早期的Web瀏覽器可以使用Javascript動態格式化HTML,其擴展了字符串的標準,實現了一些專門用于簡化常見HTML格式化任務的方法;但是,盡量不要使用這些方法,因為它們創建的標記通常無法表達語義;
示例:檢測上傳文件后綴名:
<input type="file" id="myFile"/>
<input type="button" value="上傳" onclick="upFile()" />
<script>
function upFile(){
var fileName = document.getElementById('myFile').value;
if(fileName){
// console.log(fileName);
var pos = fileName.lastIndexOf('.'); // 找到路徑中最后出現“.”的位置
// console.log(pos);
var suffix = fileName.substr(pos + 1); // 找到后綴名
// console.log(suffix);
var suffixArr = ['jpg','png','gif']; // 圖片格式數組
// console.log((suffixArr.indexOf(suffix)));
if(suffixArr.indexOf(suffix) >= 0){ // 后綴名與格式數組作比較
// 上傳圖片的操作處理
console.log("上傳成功");
}else{
console.log("圖片格式不正確");
return;
}
}
}
</script>
示例:過濾臟話:
// 簡單過濾
var str = "不要相信女人,女人太壞了";
var arr = ["壞","笨","傻"]; // 敏感字
for(var i = 0; i<arr.length; i++){
str = str.replace(arr[i],"*");
}
console.log(str);
// 復雜的過濾
var arrStr = [
"傻子,你的腦子是不是有病?你就是傻子一個!",
"你的眼睛是不是瞎了?",
"你是一個大壞蛋!"
];
var arr = ["病","傻子","瞎了","壞蛋"];
for(var i =0;i<arrStr.length; i++){
for(var j=0;j<arr.length; j++){
arrStr[i] = arrStr[i].replace(arr[j],"**");
// var reg = new RegExp(arr[j],"img");
// arrStr[i] = arrStr[i].replace(reg,"**");
}
}
console.log(arrStr);
Web前端開發之Javascript-零點程序員-王唯
文源代碼獲取:文末
啟動一個新項目并從頭開始處理身份驗證和授權可能會筋疲力盡。它通常涉及創建登錄和注冊功能,這可能很耗時。管理刷新令牌和實現雙因素身份驗證等挑戰增加了復雜性。
值得慶幸的是,隨著 .NET 8 的到來,這些任務大大簡化,只需要最少的配置。
在本文中,我將指導你完成各種標識配置方案,包括:
基本注冊和登錄。
使用持有者令牌保護終結點。
檢索用戶信息。
電子郵件確認。
重新發送電子郵件確認。
更改默認設置并添加自定義用戶屬性。
配置雙因素身份驗證 (MVC)。
先決條件:.NET 8 或更高版本。
在繼續之前,讓我概述一下我當前的環境設置:
.NET Web API 應用程序。
Visual Studio 2022 年。
利用SQLite模擬真實數據庫,而不是內存數據庫。
接下來,讓我們安裝必要的 NuGet 包。
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Tools
首先,首先創建所有身份生成的表的存儲位置。
// IdentityUser is the Microsoft base identity class.
// creates empty scheme with just all the identity tables.
class AppDbContext : IdentityDbContext<IdentityUser>
{
public AppDbContext(DbContextOptions options) : base(options)
{
}
}
接下來,我們將注冊身份驗證和授權中間件,從而靈活地在持有者令牌或 Cookie 之間進行選擇。在此示例中,我們將選擇持有者令牌,以便于在登錄時檢索持有者令牌。
// Program.cs
builder.Services.AddAuthentication()
.AddBearerToken(IdentityConstants.BearerScheme);
builder.Services.AddAuthorizationBuilder();
之后,我們將注冊我們的 ,在此示例中,它是一個 SQLite 數據庫。
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options=>
{
options.UseSqlite("DataSource=app.db");
});
之后,我們需要將 作為身份存儲連接,以便所有操作都指向 SQLite 數據庫。
// Program.cs
builder.Services.AddIdentityCore\<IdentityUser>()
.AddEntityFrameworkStores\<AppDbContext>()
.AddApiEndpoints();
最后,我們需要將所有端點添加為應用程序端點的一部分。這可以通過在構建生成器后映射標識終結點來實現。
app.MapIdentityApi<IdentityUser>();
您的Program.cs文件現在應類似于此結構。
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
var builder=WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAuthentication()
.AddBearerToken(IdentityConstants.BearerScheme);
builder.Services.AddAuthorizationBuilder();
builder.Services.AddDbContext<AppDbContext>(options=>
{
options.UseSqlite("DataSource=app.db");
});
builder.Services.AddIdentityCore<IdentityUser>()
.AddEntityFrameworkStores<AppDbContext>()
.AddApiEndpoints();
var app=builder.Build();
app.MapIdentityApi<IdentityUser>();
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
在運行應用程序之前,請不要忘記添加新的遷移,并通過從包管理器控制臺執行以下命令來更新數據庫。
Add-Migration initial
Update-Database
現在,運行應用程序并打開 Swagger。您應該會看到自動生成的終結點集合。
生成的身份終結點。
嘗試注冊新用戶。如果嘗試使用無效的電子郵件地址或不符合條件的密碼創建新用戶,則標識將返回錯誤以及發生的具體詳細信息。
現在,讓我們繼續登錄過程。如果提供的電子郵件或密碼無效,則身份將返回 401 未授權狀態。
將身份驗證配置為返回持有者令牌后,我們需要將此持有者令牌用于任何需要授權的受保護終結點。
必須注意的是,生成的令牌不是標準的 JSON Web 令牌 (JWT)。此決定是有意為之的,因為內置標識主要用于簡單方案。令牌選項并不意味著充當功能齊全的身份服務提供商或令牌服務器,而是無法使用 cookie 的客戶端的 cookie 選項的替代方案。
現在,讓我們嘗試訪問授權端點,特別是 /manage/info 端點。如果您嘗試從 Swagger 訪問它,它將顯示未經授權的錯誤。接下來,使用 Postman 登錄,保存 accessToken,并在調用 /manage/info 端點時包含它。這一次,它應該返回所需的結果。
要隨時檢索當前登錄用戶的信息,請使用存儲當前登錄用戶信息的類。ClaimsPrincipal
要使用最小 API 方法實現此目的,請執行以下操作:
app.Map("/", (ClaimsPrincipal user)=> $"Hello {user.Identity!.Name}")
.RequireAuthorization();
對于控制器方法:
[ApiController]
[Route("[controller]")]
public class TestController: ControllerBase
{
[HttpGet]
[Authorize]
public string Get()
{
return User.Identity!.Name;
}
}
請務必注意,上述示例使用“**!”**運算符,這意味著標識應始終存在,而不可能為 。此假設成立,因為兩者都是需要事先登錄的授權端點。但是,建議驗證標識是否存在,以防止潛在的 引用異常。
Microsoft建議使用SendGrid或其他電子郵件服務發送電子郵件,而不是SMTP。SMTP 很難保護和正確設置。
在本教程中,SendGrid 用于發送電子郵件。發送電子郵件需要 SendGrid 帳戶和密鑰。請參閱免費開始使用 SendGrid,注冊免費的 SendGrid 帳戶。
1- 在文件中配置 SendGrid 所需信息。避免將這些設置直接添加到appsettings.json文件中,以降低安全風險。secrets.json
{
"SendGridKey": "your send-grid key",
"From": "your registered email",
"Name": "your registered name"
}
注冊電子郵件和姓名
SendGrid API 密鑰
2- 安裝必要的 NuGet 包。
dotnet add package SendGrid
dotnet add package SendGrid.Extensions.DependencyInjection
3-實現IEmailSender
要實現,請使用類似于以下內容的代碼進行創建:IEmailSenderEmailSender.cs
using Microsoft.AspNetCore.Identity.UI.Services;
using SendGrid;
using SendGrid.Helpers.Mail;
namespace Identity;
public class EmailSender : IEmailSender
{
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
public EmailSender(IConfiguration configuration, ILogger<EmailSender> logger)
{
_configuration=configuration;
_logger=logger;
}
public async Task SendEmailAsync(string toEmail, string subject, string message)
{
var sendGridKey=_configuration["SendGridKey"];
ArgumentException.ThrowIfOrEmpty(sendGridKey, nameof(sendGridKey));
await Execute(sendGridKey, subject, message, toEmail);
}
public async Task Execute(string apiKey, string subject, string message, string toEmail)
{
var client=new SendGridClient(apiKey);
var msg=new SendGridMessage()
{
From=new EmailAddress(_configuration["From"], _configuration["Name"]),
Subject=subject,
PlainTextContent=message,
HtmlContent=message
};
msg.AddTo(new EmailAddress(toEmail));
// Disable click tracking.
// See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
msg.SetClickTracking(false, false);
var response=await client.SendEmailAsync(msg);
_logger.LogInformation(response.IsSuccessStatusCode
? $"Email to {toEmail} queued successfully!"
: $"Failure Email to {toEmail}");
}
}
4- 將 EmailService 注入其中以啟用其使用。Program.cs
// Program.cs
builder.Services.AddTransient<IEmailSender, EmailSender>();
5- 強制登錄確認并集成 SendGrid 服務。
現在,如果您嘗試再次注冊,它將起作用,但您會注意到沒有發送電子郵件確認。此外,用戶無需任何確認即可登錄。
為了解決這個問題并確保正確的流程,我們需要首先在簽名過程中要求電子郵件確認。然后,我們需要集成 SendGrid 服務,以便標識框架可以利用它。
// Program.cs
builder.Services.AddIdentityCore<IdentityUser>(options=>
{
options.SignIn.RequireConfirmedEmail=true;
})
builder.Services.AddSendGrid(options=>
options.ApiKey=builder.Configuration["SendGridKey"]!
);
您的文件現在應類似于此結構。Program.cs
using Identity;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using SendGrid.Extensions.DependencyInjection;
using System.Security.Claims;
var builder=WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAuthentication()
.AddBearerToken(IdentityConstants.BearerScheme);
builder.Services.AddSendGrid(options=>
options.ApiKey=builder.Configuration["SendGridKey"]!
);
builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.AddAuthorizationBuilder();
builder.Services.AddDbContext<AppDbContext>(options=>
{
options.UseSqlite("DataSource=app.db");
});
builder.Services.AddIdentityCore<IdentityUser>(options=>
{
options.SignIn.RequireConfirmedEmail=true;
})
.AddEntityFrameworkStores<AppDbContext>()
.AddApiEndpoints();
var app=builder.Build();
app.MapIdentityApi<IdentityUser>();
app.Map("/", (ClaimsPrincipal user)=> $"Hello {user.Identity!.Name} from minimal apis.")
.RequireAuthorization();
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
現在,是時候測試我們的應用程序了。嘗試注冊一個新用戶,并檢查您的電子郵件以獲取確認鏈接。單擊該鏈接應將數據庫中的確認狀態更改為 TRUE。
請記住檢查您的垃圾郵件文件夾,因為電子郵件通常最終會在那里。此外,請注意,電子郵件在發送之前會排隊,因此預計會有幾秒鐘的輕微延遲。
注冊新用戶
Visual Studio 日志
在數據庫中創建的用戶
確認鏈接電子郵件
通過單擊鏈接“單擊此處”,它應該將您重定向回 localhost,并將用戶在數據庫中的確認狀態更新為 TRUE。
確認消息
確認狀態更改為 TRUE
最后,通過注入 SendGrid 服務,我們可以簡化 EmailService,從而減少代碼行數。
這是該版本的更新版本,可以有效處理所有測試方案。EmailService
using Microsoft.AspNetCore.Identity.UI.Services;
using SendGrid;
using SendGrid.Helpers.Mail;
public class EmailSender : IEmailSender
{
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
private readonly ISendGridClient _sendGridClient;
public EmailSender(IConfiguration configuration, ILogger<EmailSender> logger, ISendGridClient sendGridClient)
{
_configuration=configuration;
_logger=logger;
_sendGridClient=sendGridClient;
}
public async Task SendEmailAsync(string toEmail, string subject, string message)
{
var msg=new SendGridMessage()
{
From=new EmailAddress(_configuration["From"], _configuration["Name"\]),
Subject=subject,
PlainTextContent=message,
HtmlContent=message
};
msg.AddTo(new EmailAddress(toEmail));
var response=await _sendGridClient.SendEmailAsync(msg);
_logger.LogInformation(response.IsSuccessStatusCode
? $"Email to {toEmail} queued successfully!"
: $"Failure Email to {toEmail}");
}
}
您當然需要重新發送確認電子郵件的選項,這對任何應用程序都至關重要。.NET 8 Identity 提供重新發送確認終結點:只需調用并設置正文,如下所示:.NET 8 Identity provides a resend confirmation endpoint: just call and set the body as follows:/resendConfirmationEmail
{
"Email": "user email"
}
Microsoft 標識還提供修改默認設置或引入新的自定義屬性的功能。
調整默認設置。
builder.Services.Configure<IdentityOptions>(options=>
{
// Password settings.
options.Password.RequireDigit=true;
options.Password.RequireLowercase=true;
options.Password.RequireNonAlphanumeric=true;
options.Password.RequireUppercase=true;
options.Password.RequiredLength=6;
options.Password.RequiredUniqueChars=1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan=TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts=5;
options.Lockout.AllowedForNewUsers=true;
// User settings.
options.User.AllowedUserNameCharacters="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.\_@+";
options.User.RequireUniqueEmail=false;
});
將新的自定義屬性添加到用戶屬性中。這可以通過創建一個新的用戶類來實現,該類繼承自 。
class AppUser : IdentityUser
{
public int MyProperty { get; set; }
}
隨后,需要添加新的遷移,并且應將新屬性集成到表中。
請記住將應用程序中出現的每個項更新為IdentityUserAppUser
在我們開始之前,這里有一些概念,在我們開始之前最好知道。
什么是多重身份驗證 (MFA)?
多重身份驗證 (MFA) 通過要求用戶在登錄過程中提供其他形式的身份標識來增強安全性。這可能包括從手機輸入代碼、使用 FIDO2 密鑰或提供指紋掃描。通過要求第二種形式的身份驗證,MFA 使攻擊者更難獲得未經授權的訪問,因為附加因素不容易獲得或復制。
什么是雙因素身份驗證 (2FA)?
雙因素身份驗證 (2FA) 類似于 MFA 的子集,但不同之處在于 MFA 可能需要兩個或多個因素來證明身份。
什么是TOTP(基于時間的一次性密碼算法)?
使用 ASP.NET Core Identity 時,默認支持使用 TOTP 的 MFA。此方法可與任何合規的身份驗證器應用一起使用,包括:
Microsoft 身份驗證器
谷歌身份驗證器
什么是 MFA 短信?
與密碼身份驗證(單因素)相比,帶有 SMS 的 MFA 大大提高了安全性。但是,不再建議使用 SMS 作為第二個因素。對于此類實現,存在太多已知的攻擊媒介。
什么是在線快速身份識別 (FIDO)?
FIDO 是無密碼身份驗證的開放標準,允許用戶在沒有密碼的情況下登錄。FIDO2 密鑰(通常是 USB,但也包括藍牙或 NFC)通過消除密碼風險來增強安全性。
它們目前被認為是最安全的 MFA 方法。
現在,讓我們開始配置 2FA。
我們將利用 2FA 與 MVC 項目的集成。首次創建新的 MVC 項目時,可以選擇從一開始就生成標識系統,包括“注冊”、“登錄”和“2FA”。我們的第一步是創建一個新的 MVC 項目。
在 Visual Studio 2022 中,可以完成以下操作:
選擇個人帳戶標識選項。
或者,您可以使用命令行。
dotnet new mvc -n "2FA" -au individual
創建的項目應如下所示。
現在,運行創建的項目。應顯示一個帶有登錄和注冊按鈕的歡迎頁面。
單擊“注冊”以創建新用戶。
單擊“單擊此處確認您的帳戶”以確認您的電子郵件。
接下來,注銷,然后再次登錄。點擊右上角的電子郵件。
導航到“雙因素身份驗證”部分。
單擊“添加身份驗證器應用程序” 應顯示雙因素身份驗證設置屏幕。
如您所見,它是開箱即用的,但沒有掃描二維碼的選項;它僅適用于代碼。
現在,讓我們添加二維碼功能,允許用戶只需掃描二維碼即可自動添加密鑰。
我們需要訪問為我們創建的生成的 cshtml 頁面。默認情況下,這些頁面位于目錄中。但是,如果尚未展開目錄,則 dotnet 不會顯示文件,除非生成了文件。Areas/Identity/Pages
為此,請運行該命令。
dotnet tool install -g dotnet-aspnet-codegenerator
接下來,我們需要安裝所有必需的 NuGet 包。
dotnet add pacakge Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
然后,運行代碼生成器并指定要檢索的頁面。默認情況下,如果將其留空,它將生成許多頁面。但是,為了避免不必要的頁面使您的項目混亂,我們將指定我們需要的特定頁面。
cd "your project directory"
dotnet aspnet-codegenerator identity -dc _2FA.Data.ApplicationDbContext --files "Account.Manage.EnableAuthenticator"
若要查看可生成的所有文件,請運行該命令。
dotnet aspnet-codegenerator identity -dc _2FA.Data.ApplicationDbContext -lf
以下是可以生成的所有文件的完整列表。
File List:
Account.\_StatusMessage
Account.AccessDenied
Account.ConfirmEmail
Account.ConfirmEmailChange
Account.ExternalLogin
Account.ForgotPassword
Account.ForgotPasswordConfirmation
Account.Lockout
Account.Login
Account.LoginWith2fa
Account.LoginWithRecoveryCode
Account.Logout
Account.Manage.\_Layout
Account.Manage.\_ManageNav
Account.Manage.\_StatusMessage
Account.Manage.ChangePassword
Account.Manage.DeletePersonalData
Account.Manage.Disable2fa
Account.Manage.DownloadPersonalData
Account.Manage.Email
Account.Manage.EnableAuthenticator
Account.Manage.ExternalLogins
Account.Manage.GenerateRecoveryCodes
Account.Manage.Index
Account.Manage.PersonalData
Account.Manage.ResetAuthenticator
Account.Manage.SetPassword
Account.Manage.ShowRecoveryCodes
Account.Manage.TwoFactorAuthentication
Account.Register
Account.RegisterConfirmation
Account.ResendEmailConfirmation
Account.ResetPassword
Account.ResetPasswordConfirmation
返回到 Visual Studio,現在應該會看到已添加的文件。EnableAuthenticator.cshtml
在編輯文件之前,我們需要下載qrcode.js JavaScript庫,它將為我們渲染并生成QR碼。將其保存在文件夾中。EnableAuthenticator.cshtmlwwwroot\lib
在這里,我將其重命名為qrcodejs
接下來,創建一個新文件,使用以下代碼在 中調用它。qr.jswwwroot\js
window.addEventListener("load", ()=> {
const uri=document.getElementById("qrCodeData").getAttribute('data-url');
new QRCode(document.getElementById("qrCode"),
{
text: uri,
width: 150,
height: 150
});
});
最后,打開文件,然后:EnableAuthenticator.cshtml
更新該部分以添加對以前下載的庫的引用。Scriptsqrcode.js
添加帶有調用的文件以生成二維碼。qr.js
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript" src="~/lib/qrcodejs/qrcode.js"></script>
<script type="text/javascript" src="~/js/qr.js"></script>
}
再次運行應用程序,QR 碼現在應該出現。
使用Authenticator應用程序掃描它,它應該可以完美運行。
通過將其添加到身份驗證器應用,可以從應用中生成的代碼中驗證代碼。它應該顯示 2FA 確認。
現在,嘗試注銷,然后重新登錄。將出現 2FA 表單,提示您輸入 2FA 代碼。
恭喜,您的 2FA 設置完美。
源代碼獲取:公眾號回復消息【code:19129
】
*請認真填寫需求信息,我們會在24小時內與您取得聯系。