認識Closure前,要先知道 Lexical Scope!
Lexical Scope 定義函式與其可使用的變數範圍
若fn2包在fn1內,fn2可以使用fn1的變數,這就是fn2的Lexical Scope。
子層可以拿父層,父層無法拿子層。
Lexical Scope範例:
// global scope
let x = 1;
const parentFunction = ()=>{
//local scope
let y = 2;
console.log(x);
console.log(y);
const childFunction = ()=>{
console.log(x += 5);
console.log(y += 1);
}
childFunction(); //6,3
}
parentFunction();// 1,2
可以說,childFunction在parentFunction中有Closure,因為ChildFunction可以拿取外層(也就是parentFunction)的變數,但是Lexical Scope不是代表Closure,只算是Closure中重要的一部份。
Closure 在我們定義函式時就已經出現,不是在執行時才出現。
前面說 Lexical Scope 是Closure重要的一部分,那另一部份呢?
Closure: 一個函式可以取得父層的scope,即使父層已經執行結束。
如何讓子層函式可以繼續取得父層scope,就算父層已經結束執行呢?
我們可以修改上一個範例:
將原本執行childFunction的地方,改為回傳 childFunction本身。
// global scope
let x = 1;
const parentFunction = ()=>{
//local scope
let y = 2;
console.log(x);
console.log(y);
const childFunction = ()=>{
console.log(x += 5);
console.log(y += 1);
}
return childFunction;
}
const result = parentFunction();//1,2
console.log(result);
//()=>{console.log(x += 5);console.log(y += 1);}
result(); //6,3
result(); //11,4
console.log(x); //11
console.log(y); //reference error, private variable
const increment = (()=>{
let counter = 0;
console.log(`counter ${counter}`);
const credit = (num) => console.log(`I have ${num} credit(s).`);
return ()=>{conter++; credit(counter)}
})();
// counter 0
increment(); // I have 1 credit(s).
increment(); // I have 2 credit(s).
const credits = ((num)=>{
let credits = num;
console.log(`initial credits value:` ${credits});
return ()=>{
credit -= `;
if(credits > 0){
console.log(`playing game, ${credits} credits remaining`)
};
if(credits <= 0){
console.log(`not enough credits`)
}
}
})(3); // initial credits value: 3
cresits() // playing game,2 credits remaining
cresits() // playing game,1 credits remaining
cresits() // not enough credits
]]>認識Closure前,要先知道 Lexical Scope!
Lexical Scope 定義函式與其可使用的變數範圍
若fn2包在fn1內,fn2可以使用fn1的變數,這就是fn2的Lexical Scope。
子層可以拿父層,父層無法拿子層。
Lexical Scope範例:
// global scope
let x = 1;
const parentFunction = ()=>{
//local scope
let y = 2;
console.log(x);
console.log(y);
const childFunction = ()=>{
console.log(x += 5);
console.log(y += 1);
}
childFunction(); //6,3
}
parentFunction();// 1,2
可以說,childFunction在parentFunction中有Closure,因為ChildFunction可以拿取外層(也就是parentFunction)的變數,但是Lexical Scope不是代表Closure,只算是Closure中重要的一部份。
Closure 在我們定義函式時就已經出現,不是在執行時才出現。
前面說 Lexical Scope 是Closure重要的一部分,那另一部份呢?
Closure: 一個函式可以取得父層的scope,即使父層已經執行結束。
如何讓子層函式可以繼續取得父層scope,就算父層已經結束執行呢?
我們可以修改上一個範例:
將原本執行childFunction的地方,改為回傳 childFunction本身。
// global scope
let x = 1;
const parentFunction = ()=>{
//local scope
let y = 2;
console.log(x);
console.log(y);
const childFunction = ()=>{
console.log(x += 5);
console.log(y += 1);
}
return childFunction;
}
const result = parentFunction();//1,2
console.log(result);
//()=>{console.log(x += 5);console.log(y += 1);}
result(); //6,3
result(); //11,4
console.log(x); //11
console.log(y); //reference error, private variable
const increment = (()=>{
let counter = 0;
console.log(`counter ${counter}`);
const credit = (num) => console.log(`I have ${num} credit(s).`);
return ()=>{conter++; credit(counter)}
})();
// counter 0
increment(); // I have 1 credit(s).
increment(); // I have 2 credit(s).
const credits = ((num)=>{
let credits = num;
console.log(`initial credits value:` ${credits});
return ()=>{
credit -= `;
if(credits > 0){
console.log(`playing game, ${credits} credits remaining`)
};
if(credits <= 0){
console.log(`not enough credits`)
}
}
})(3); // initial credits value: 3
cresits() // playing game,2 credits remaining
cresits() // playing game,1 credits remaining
cresits() // not enough credits
]]>
IIFE 立即函式(Immediately-Invoked Function Expression)
(function(){})()
(function(){}());
(()=>{})();
(function fn1(){})()
(function fn1(){
num++;
console.log(num);
return num !== 5 ? fn1(num) : console.log('finished!')
})(num = 0)
主要兩點:
()
執行()
、+
、-
、void
、new
等運算子將其轉換為函式表達式,然後再加上()
立即執行。例如:
void function(){}(alert("ok"));
在最後呼叫的"()"傳入需要的引數
1.當我們需要寫一個js檔案,並且複用率很高的時候,建議使用。
2.如果宣告的函式只需要呼叫一次,建議使用。
3.獨立模組,這個和第一點差不多。單獨提出來,是想強調一下立即執行函式的好處,開發時,它能做到各模組的低耦合,減少對全域性作用域的汙染
const x = 'global'
(()=>{
const x = 'IIFE';
console.log(x)
})(); // IIFE
console.log(x) // global
const increment = (()=>{
let counter = 0;
console.log(`counter ${counter}`);
const credit = (num) => console.log(`I have ${num} credit(s).`);
return ()=>{conter++; credit(counter)}
})(); // counter 0
increment(); // I have 1 credit(s).
increment(); // I have 2 credit(s).
//建立object
const Score = (()=>{
let count = 0;
return{
current: ()=>{return count},
increment: ()=>{count++},
reset: ()=>{count = 0}
}
})();
Score.increment();
cosole.log(Score.current()) // 1
Score.reset();
cosole.log(Score.current()) // 0
const Game = (()=>{
let count = 0;
const current = ()=>{ return `Game score is ${count}.`};
const increment = ()=>{ count ++};
const reset = ()=>{ count = 0};
return{
current: current,
increment: increment,
reset: reset
}
})();
Game.increment();
console.log(Game.current());//1
(( namespace )=>{
namespace.count = 0;
namespace.current = function(){ return `App count is ${this.count}`};
//不使用arrow,是因為要運用到this
namespace.increment = function(){ this.count++ };
namespace.reset = function(){
this.count = 0};
})(window.App = windowApp || {});
App.increment();
console.log(App.current())// App count is 1
]]>IIFE 立即函式(Immediately-Invoked Function Expression)
(function(){})()
(function(){}());
(()=>{})();
(function fn1(){})()
(function fn1(){
num++;
console.log(num);
return num !== 5 ? fn1(num) : console.log('finished!')
})(num = 0)
主要兩點:
()
執行()
、+
、-
、void
、new
等運算子將其轉換為函式表達式,然後再加上()
立即執行。例如:
void function(){}(alert("ok"));
在最後呼叫的"()"傳入需要的引數
1.當我們需要寫一個js檔案,並且複用率很高的時候,建議使用。
2.如果宣告的函式只需要呼叫一次,建議使用。
3.獨立模組,這個和第一點差不多。單獨提出來,是想強調一下立即執行函式的好處,開發時,它能做到各模組的低耦合,減少對全域性作用域的汙染
const x = 'global'
(()=>{
const x = 'IIFE';
console.log(x)
})(); // IIFE
console.log(x) // global
const increment = (()=>{
let counter = 0;
console.log(`counter ${counter}`);
const credit = (num) => console.log(`I have ${num} credit(s).`);
return ()=>{conter++; credit(counter)}
})(); // counter 0
increment(); // I have 1 credit(s).
increment(); // I have 2 credit(s).
//建立object
const Score = (()=>{
let count = 0;
return{
current: ()=>{return count},
increment: ()=>{count++},
reset: ()=>{count = 0}
}
})();
Score.increment();
cosole.log(Score.current()) // 1
Score.reset();
cosole.log(Score.current()) // 0
const Game = (()=>{
let count = 0;
const current = ()=>{ return `Game score is ${count}.`};
const increment = ()=>{ count ++};
const reset = ()=>{ count = 0};
return{
current: current,
increment: increment,
reset: reset
}
})();
Game.increment();
console.log(Game.current());//1
(( namespace )=>{
namespace.count = 0;
namespace.current = function(){ return `App count is ${this.count}`};
//不使用arrow,是因為要運用到this
namespace.increment = function(){ this.count++ };
namespace.reset = function(){
this.count = 0};
})(window.App = windowApp || {});
App.increment();
console.log(App.current())// App count is 1
]]>
載入json-server
npm install -g json-server
啟用json-server並指向JSON檔案
json-server --watch db.json
可以測試 GET/POST/PUT/DELETE
GET/posts
/users
是正確的,但是/getusers
就是錯誤的因為接下來的練習要用axios,所以也提一下!
Axios是一個基於promise的HTTP庫,可以用在瀏覽器或node.js中。
基本上有五種請求方式:
axios.get("http://localhost:3000/posts")
.then(response => {
console.log(response.data)
posts.value.push(...response.data)
})
.catch(error => console.log(error))
//第一行也可以這樣寫
//axios.get("http://localhost:3000/posts",{params: { ID: 1 }})
axios.get("http://localhost:3000/posts/1")
.then(response => {
console.log(response.data)
posts.value.push(...response.data)
})
.catch(error => console.log(error))
axios({
method: "post",
url: "http://localhost:3000/posts",
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
data: {
title: post.value.title,
body: post.value.body
}
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
axios({
method: "delete",
url: `http://localhost:3000/posts/${id}`,
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
]]>載入json-server
npm install -g json-server
啟用json-server並指向JSON檔案
json-server --watch db.json
可以測試 GET/POST/PUT/DELETE
GET/posts
/users
是正確的,但是/getusers
就是錯誤的因為接下來的練習要用axios,所以也提一下!
Axios是一個基於promise的HTTP庫,可以用在瀏覽器或node.js中。
基本上有五種請求方式:
axios.get("http://localhost:3000/posts")
.then(response => {
console.log(response.data)
posts.value.push(...response.data)
})
.catch(error => console.log(error))
//第一行也可以這樣寫
//axios.get("http://localhost:3000/posts",{params: { ID: 1 }})
axios.get("http://localhost:3000/posts/1")
.then(response => {
console.log(response.data)
posts.value.push(...response.data)
})
.catch(error => console.log(error))
axios({
method: "post",
url: "http://localhost:3000/posts",
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
data: {
title: post.value.title,
body: post.value.body
}
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
axios({
method: "delete",
url: `http://localhost:3000/posts/${id}`,
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
]]>
REST是Roy Thomas Fielding在他2000年的博士論文中提出的。
Fielding是HTTP協議(1.0/1.1版)的主要設計者、Apache服務器軟體的作者之一。
他的論文題到:
網路研究主要關注系統間通信行為的細節、如何改進特定通信機制的表現,卻常常忽略一個事實,那就是改變應用程式的互動風格比改變互動協議,對整體表現有更大的影響。我照篇文章的主要目的,就是想在符合架構原理的前提下,理解和評估以網路為基礎應用軟體的架構設計,得到一個功能強、性能好、適宜通信的架構。
Fielding將這個架構原則(架構風格)命名為 REST(Representational State Transfer)。
其實省略了Resources,整個中文翻譯會是
資源表現層狀態轉化
只要符合REST原則的架構,就是RESTful架構。
讓我們分字來理解REST
網路上的任何一個實體都是資源,例如:一張圖片、一首歌、一段文本、一個服務...,都是一個資源。
資源可以用一個URI(統一資源標識符 Uniform Resource Identifier)指向它,每種資源都對應一個特定的URI。
而URL提供尋找該事物的方法(URL就是定位符, Locator)
將資源具體呈現出來的形式,就是資源的表現層
例如: 文本的表現層有 txt、HTML、XML、JSON;圖片有JPG、PNG...
在HTTP請求的訊息中用Accept和Content-Type指定的,就是對表現層描述。
就是客戶端對伺服器端運用某個手段,讓伺服器端產生狀態轉化,而這種轉化會發生在表現層上,所以稱為資源表現層狀態轉化
(客戶端運用的手段就像: POST、GET、PUT、DELETE)
REST是Roy Thomas Fielding在他2000年的博士論文中提出的。
Fielding是HTTP協議(1.0/1.1版)的主要設計者、Apache服務器軟體的作者之一。
他的論文題到:
網路研究主要關注系統間通信行為的細節、如何改進特定通信機制的表現,卻常常忽略一個事實,那就是改變應用程式的互動風格比改變互動協議,對整體表現有更大的影響。我照篇文章的主要目的,就是想在符合架構原理的前提下,理解和評估以網路為基礎應用軟體的架構設計,得到一個功能強、性能好、適宜通信的架構。
Fielding將這個架構原則(架構風格)命名為 REST(Representational State Transfer)。
其實省略了Resources,整個中文翻譯會是
資源表現層狀態轉化
只要符合REST原則的架構,就是RESTful架構。
讓我們分字來理解REST
網路上的任何一個實體都是資源,例如:一張圖片、一首歌、一段文本、一個服務...,都是一個資源。
資源可以用一個URI(統一資源標識符 Uniform Resource Identifier)指向它,每種資源都對應一個特定的URI。
而URL提供尋找該事物的方法(URL就是定位符, Locator)
將資源具體呈現出來的形式,就是資源的表現層
例如: 文本的表現層有 txt、HTML、XML、JSON;圖片有JPG、PNG...
在HTTP請求的訊息中用Accept和Content-Type指定的,就是對表現層描述。
就是客戶端對伺服器端運用某個手段,讓伺服器端產生狀態轉化,而這種轉化會發生在表現層上,所以稱為資源表現層狀態轉化
(客戶端運用的手段就像: POST、GET、PUT、DELETE)
HTTP就是一個客戶端發請求,伺服器端回應的一個協議。基於TPC的連接方式。
http://host[":"port][abs_path]
host: 合法的網路主機域名或IP位址
port: 一個端口號(為空的話則為80)
abs_path: 如果沒有給abs_path,當作為請求URL時,會以/
這個形式給出。
請求包含三區塊:
例如:
GET / HTTP/1.1
Host: developer.mozilla.org
Accept-Language: fr
為什麼需要cache 快取?
cache的用處是為了節省流量、節省時間然後節省資源耗損。
如果今天每一個訪客到首頁都要讓你重新到資料庫抓資料,這樣對資料庫的負擔會很大,假如說這個網站並不是短期之內會有變動的,那就可以使用cache快取(也可以說緩存)。
簡單來說,就是第一次撈資料後,就先把資料存到某個地方,之後如果需要,就可以用極快的方式取到,也不用要重新請求資料庫。
要達成瀏覽器先把照片Cache起來,可以在HTTP Response Header 裡面加上一個Expires
,裡面就是這個 Cache 到期的時間,例如:
Expires: Sat, 31 Dec 2022 00:00:00 GMT
如果沒有超過這個時間,瀏覽器就不會從後端取資料,而是直接從電腦存好的cache拿。
這個時間是以電腦的時間為基準,所以如果刻意更改時間,讓他超過expires date,瀏覽器就會在發送新的request。
為了應對上面的問題,就有了cache control(cache control也不只解決這個問題)。
其中的一個用法就是 max-age
,max-age
和expired
一樣,都是可以用來設定cache過期時間,但是expired
設定到期日期,而max-age
設定從第一次抓資料後,經過多少秒數會過期。
如果同時設定了expired
和max-age
,會看max-age
喔!
如果是機密資料,不想要任何快取呢?
可以使用cache-control: no-store
。
cache到期後,有兩條路
要做到這樣的判斷,需要在客戶端第一次抓資料時,伺服器傳回response的Header上加上Last-Modified
,來標示這個檔案最後的更改時間。
等到下一次客戶端要在取資料時,可以再request的header上加上If-Modified-Since
,來告訴伺服器,上次的資料最後修改日期,伺服器就會判斷在這之後有沒有修改,若有就會給客戶新資料,若無,就會回傳Status code: 304 (Not Modified)
。
上面這個方法是用修改日期來看,但是修改的定義,只要將檔案打開再儲存關起來,就算沒改內容也算是修改。
所以有另一個方法是看檔案內容有沒有一樣。
Etag
和 If-None-Match
伺服器第一次傳資料回應時,會帶上Etag
(檔案獨有的),等cache過期,瀏覽器會再發送帶有If-None-Match
的請求,伺服器判斷有沒有相同。
max-age
和Etag
Cache-Control: max-age=0
,讓客戶一拿到資料,0秒後cache就過期,等客戶再造訪網頁(重整..)後,就會再重抓資料,但是因為同時也有附上Etag
。所以客戶端的請求會帶上If-None-Match
,伺服器就可以判斷是否需要重新傳送。Cache-control: no-cache
Cache-control: no-store
不同,Cache-control: no-store
是永遠不要快存。Cache-control: no-cache
是永遠檢查快存。最後還會遇到一個問題,因為現在通常會做SPA架構,透過Webpack打包,只需要引入script就好。
通常index.html的body內部是空的,這樣要怎麼讓客戶端發現內容有更動呢?
解決方法就是,將Etag寫入引入的script名稱(例如:script-123abc.js 下一次更改時改為script-456def.js)
HTTP請求的Content-Type會出現在POST或PUT時,讓客戶端告訴伺服器自己傳的資料是什麼內容類型。
HTTP/1.1 200 OK
Date: Sat, 09 Oct 2010 14:28:02 GMT
Server: Apache
Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT
ETag: "51142bc1-7449-479b075b2891b"
Accept-Ranges: bytes
Content-Length: 29769
Content-Type: text/html
在回應中Content-Type 用來表示資源的 media type。
瀏覽器有時會自己推測內容類型(MIME sniffing),如果要阻止這個行為,可以在回應中設定 X-Content-Type-Options 標頭為 nosniff。
HTTP就是一個客戶端發請求,伺服器端回應的一個協議。基於TPC的連接方式。
http://host[":"port][abs_path]
host: 合法的網路主機域名或IP位址
port: 一個端口號(為空的話則為80)
abs_path: 如果沒有給abs_path,當作為請求URL時,會以/
這個形式給出。
請求包含三區塊:
例如:
GET / HTTP/1.1
Host: developer.mozilla.org
Accept-Language: fr
為什麼需要cache 快取?
cache的用處是為了節省流量、節省時間然後節省資源耗損。
如果今天每一個訪客到首頁都要讓你重新到資料庫抓資料,這樣對資料庫的負擔會很大,假如說這個網站並不是短期之內會有變動的,那就可以使用cache快取(也可以說緩存)。
簡單來說,就是第一次撈資料後,就先把資料存到某個地方,之後如果需要,就可以用極快的方式取到,也不用要重新請求資料庫。
要達成瀏覽器先把照片Cache起來,可以在HTTP Response Header 裡面加上一個Expires
,裡面就是這個 Cache 到期的時間,例如:
Expires: Sat, 31 Dec 2022 00:00:00 GMT
如果沒有超過這個時間,瀏覽器就不會從後端取資料,而是直接從電腦存好的cache拿。
這個時間是以電腦的時間為基準,所以如果刻意更改時間,讓他超過expires date,瀏覽器就會在發送新的request。
為了應對上面的問題,就有了cache control(cache control也不只解決這個問題)。
其中的一個用法就是 max-age
,max-age
和expired
一樣,都是可以用來設定cache過期時間,但是expired
設定到期日期,而max-age
設定從第一次抓資料後,經過多少秒數會過期。
如果同時設定了expired
和max-age
,會看max-age
喔!
如果是機密資料,不想要任何快取呢?
可以使用cache-control: no-store
。
cache到期後,有兩條路
要做到這樣的判斷,需要在客戶端第一次抓資料時,伺服器傳回response的Header上加上Last-Modified
,來標示這個檔案最後的更改時間。
等到下一次客戶端要在取資料時,可以再request的header上加上If-Modified-Since
,來告訴伺服器,上次的資料最後修改日期,伺服器就會判斷在這之後有沒有修改,若有就會給客戶新資料,若無,就會回傳Status code: 304 (Not Modified)
。
上面這個方法是用修改日期來看,但是修改的定義,只要將檔案打開再儲存關起來,就算沒改內容也算是修改。
所以有另一個方法是看檔案內容有沒有一樣。
Etag
和 If-None-Match
伺服器第一次傳資料回應時,會帶上Etag
(檔案獨有的),等cache過期,瀏覽器會再發送帶有If-None-Match
的請求,伺服器判斷有沒有相同。
max-age
和Etag
Cache-Control: max-age=0
,讓客戶一拿到資料,0秒後cache就過期,等客戶再造訪網頁(重整..)後,就會再重抓資料,但是因為同時也有附上Etag
。所以客戶端的請求會帶上If-None-Match
,伺服器就可以判斷是否需要重新傳送。Cache-control: no-cache
Cache-control: no-store
不同,Cache-control: no-store
是永遠不要快存。Cache-control: no-cache
是永遠檢查快存。最後還會遇到一個問題,因為現在通常會做SPA架構,透過Webpack打包,只需要引入script就好。
通常index.html的body內部是空的,這樣要怎麼讓客戶端發現內容有更動呢?
解決方法就是,將Etag寫入引入的script名稱(例如:script-123abc.js 下一次更改時改為script-456def.js)
HTTP請求的Content-Type會出現在POST或PUT時,讓客戶端告訴伺服器自己傳的資料是什麼內容類型。
HTTP/1.1 200 OK
Date: Sat, 09 Oct 2010 14:28:02 GMT
Server: Apache
Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT
ETag: "51142bc1-7449-479b075b2891b"
Accept-Ranges: bytes
Content-Length: 29769
Content-Type: text/html
在回應中Content-Type 用來表示資源的 media type。
瀏覽器有時會自己推測內容類型(MIME sniffing),如果要阻止這個行為,可以在回應中設定 X-Content-Type-Options 標頭為 nosniff。
HTTP的全名是 超文本傳輸協定(HyperText Transfer Protocol),內容規範了客戶端請求與伺服器回應的標準,藉由 TCP 作為資料的傳輸方式。
經過TCP傳遞資料時,這些資料都是明文,可能會被惡意竊聽者竊取資料。因此需要加密!!
加密就是讓原本的明文變成無法讀取內容的密文,這個密文只能透過特定的解密過程,將其回復成明文。
加密的方式例如:
這個又有人稱為「對稱式加密」,加密與解密是透過同一個金鑰。
原理大致上是將每個字元位移然後透過共用金鑰解密再移回來。
但是這不完全安全,因為在使用共用金鑰加密前,需要傳遞金鑰給對方,在傳遞的過程中,就有機會被竊聽。
這個又有人稱為「非對稱式加密」。
因為上面共用金鑰加密的問題,公開金鑰加密改變成,每個通訊者都有成對的鑰匙,一把是 公鑰、一把是私鑰。共鑰是大家都看的到,但私鑰只有自己看的到。每個資料被任意一把鑰匙加密後,必須要透過另一個配對的鑰匙才能解密。
這樣感覺很安全對吧!但是其實還是沒辦法阻止惡意竊聽。
假設今天A和B準備進行通訊,而C是竊聽者。如果A和B都把C當成通訊對方,這樣的情況下,A會使用C的公鑰加密內容,然後傳給C。C再把東西解密傳給B,反之亦然。
這樣A和B其實也不會知道C的,同時C已經竊取通訊的內容啦!
這種攻擊叫做中間人攻擊,會有這樣的問題是因為我們不知道正在加密的公鑰是屬於誰的。
為了解決上面的問題,就出現了數位憑證!
數位憑證要怎麼運作呢?
簡單來講就是由一個機構來頒發憑證,這個數位憑證內會有數位簽章與通訊內容。通訊會傳送數位憑證過去,因為有數位簽章,所以接收的人就會確定這個公鑰是誰的了!
假設今天A和B要進行通訊,在開始前A必須提供公鑰和Email去機構申請憑證。機構核可後,便會透過數位簽章包裹通訊的內容,並製作成數位憑證。
A再把數位憑證給B,而B透過數位簽章就可以確認是傳來的公鑰是A的,接著就可以加密後,再回傳。
這個s就是Secure。全名為 超文本傳輸安全協定。
HTTPS透過HTTP進行通訊,但通訊的過程使用 SSL/TLS 進行加密,藉由類似上方加密的方式,以確保資料安全傳輸。
由於非對稱加密的運算量較高,傳遞回應較慢;所以,會透過公開金鑰加密傳遞出共用的金鑰,再透過共用金鑰加密進行後續的傳遞,兼顧了安全性及傳遞速度。
Secure Sockets Layer (安全通訊端層)是TLC的前身。
Transport Layer Security(傳輸層安全性)
]]>HTTP的全名是 超文本傳輸協定(HyperText Transfer Protocol),內容規範了客戶端請求與伺服器回應的標準,藉由 TCP 作為資料的傳輸方式。
經過TCP傳遞資料時,這些資料都是明文,可能會被惡意竊聽者竊取資料。因此需要加密!!
加密就是讓原本的明文變成無法讀取內容的密文,這個密文只能透過特定的解密過程,將其回復成明文。
加密的方式例如:
這個又有人稱為「對稱式加密」,加密與解密是透過同一個金鑰。
原理大致上是將每個字元位移然後透過共用金鑰解密再移回來。
但是這不完全安全,因為在使用共用金鑰加密前,需要傳遞金鑰給對方,在傳遞的過程中,就有機會被竊聽。
這個又有人稱為「非對稱式加密」。
因為上面共用金鑰加密的問題,公開金鑰加密改變成,每個通訊者都有成對的鑰匙,一把是 公鑰、一把是私鑰。共鑰是大家都看的到,但私鑰只有自己看的到。每個資料被任意一把鑰匙加密後,必須要透過另一個配對的鑰匙才能解密。
這樣感覺很安全對吧!但是其實還是沒辦法阻止惡意竊聽。
假設今天A和B準備進行通訊,而C是竊聽者。如果A和B都把C當成通訊對方,這樣的情況下,A會使用C的公鑰加密內容,然後傳給C。C再把東西解密傳給B,反之亦然。
這樣A和B其實也不會知道C的,同時C已經竊取通訊的內容啦!
這種攻擊叫做中間人攻擊,會有這樣的問題是因為我們不知道正在加密的公鑰是屬於誰的。
為了解決上面的問題,就出現了數位憑證!
數位憑證要怎麼運作呢?
簡單來講就是由一個機構來頒發憑證,這個數位憑證內會有數位簽章與通訊內容。通訊會傳送數位憑證過去,因為有數位簽章,所以接收的人就會確定這個公鑰是誰的了!
假設今天A和B要進行通訊,在開始前A必須提供公鑰和Email去機構申請憑證。機構核可後,便會透過數位簽章包裹通訊的內容,並製作成數位憑證。
A再把數位憑證給B,而B透過數位簽章就可以確認是傳來的公鑰是A的,接著就可以加密後,再回傳。
這個s就是Secure。全名為 超文本傳輸安全協定。
HTTPS透過HTTP進行通訊,但通訊的過程使用 SSL/TLS 進行加密,藉由類似上方加密的方式,以確保資料安全傳輸。
由於非對稱加密的運算量較高,傳遞回應較慢;所以,會透過公開金鑰加密傳遞出共用的金鑰,再透過共用金鑰加密進行後續的傳遞,兼顧了安全性及傳遞速度。
Secure Sockets Layer (安全通訊端層)是TLC的前身。
Transport Layer Security(傳輸層安全性)
]]>在每一次新增或修改後,若想要將這些變動紀錄到數據庫中,需要commit上去。透過commit,數據庫會依時間記錄更動的內容等等。建議每個不同目的,例如:新增、修復... 都可以分開提交。
關於基本的數據庫設定與運用可以參考:
終端機 & Git 指令
前面我們大致上知道這些指令
git add
git commit
git status
(看看檔案庫內有幾個沒追蹤的檔案...等等)git log
git pull
那麼在和多人一起工作時,還會再有哪些常用的指令呢?
git clone 網址
若是使用SSH的話就需要另外做設定。
補充: 關於http和ssh兩種協議
git branch
分支預設的branch就是master(main),而若是多人合作,同時有人修改、有人添加新功能...,全部都在master上作業的話,應該就會大亂了吧!而且master通常會是正式版資料,為了不影響正式版,我們需要透過再開分支來測試或開發。
branch 就是指向某個 commit 的 reference, branch 其實就是在說:「我想要包含這一次的 commit 以及它的所有 parent 的 commit。」
git branch 分支名稱
git branch
git checkout 分支名稱
git branch -d 分支名稱
(-D 是強制刪除)git reset HEAD^
git merge 分支名稱
rebasing 就是取出一連串的 commit,"複製"它們,然後把它們接在別的地方。
不同於merge的地方是,rebasing比較線性,也可以更明確看到修改歷程。
假設有一個feature分支 與 main分支
我要將feature和main合併了,可以將branch移到feature分支上然後輸入git rebase bugFix
通常預設分支就是 master
開發分支 develop
開發新功能分支 feature (feature/01、feature/xxx)
首先要先了解 HEAD。
HEAD 是一個指標,指向某一個分支,通常你可以把 HEAD 當做「目前所在分支」看待。
假如說我有三次Commit,現在我的HEAD應該會在我的第三次Commit上,如果我想要回去看我的第一次Commit,我可以輸入git checkout 第一次Commit的編號
或 git checkout HEAD~2
(回兩步)。這時候我的HEAD就會指向在第一次Commit的地方。
HEAD 預設會一直跟著 master/main分支。
學習自: youtube channel 高見龍
只 Push 部份的進度是什麼意思呢? 假設今天你想每日上傳一直影片,為了保險起見,你可能預先多拍了好幾隻。今天只是Day4,但你已經拍到Day8。
如果直接使用git push origin master
,會將全部都推上去。
所以要怎麼只推Day4的就好了?
首先可以要先理解一下 git push origin master
的概念。
這個指令的完整版其實是: git push origin master: master
解構來看: git推送master這個分支到origin節點,並產生新的master分支。
所以:git push origin master: new
就是:git推送master這個分支到origin節點,並產生新的new分支。
Ok! 那麼如果只要推Day4上去呢?
git push origin Day4: master
or git push origin Day4
這個 Day4 其實會是Commit的編號
這兩個符號都是幫助我們在reset或是checkout時,作相對位置的移動。
範例:
git checkout HEAD^
:我要將現在的HEAD移動到(現在HEAD的)前一個地方git checkout HEAD^^
:我要將現在的HEAD移動到(現在HEAD的)前兩步地方git checkout HEAD~3
:我要將現在的HEAD移動到(現在HEAD的)前三步地方git checkout HEAD^^^
但是這兩者上又有些不同。假設現在這個HEAD它是合併來的,它有兩個parent node。這樣他的上一步要回去哪個呢?
如果是使用git checkout HEAD^
,它會回到第一個Parent,因為它的完整其實是git checkout HEAD^1
。
如果要回到第二個Parent呢? 要輸入git checkout HEAD^2
要特別注意是跟git checkout HEAD~2
不一樣喲!
git checkout HEAD~2
是回兩步,但是不管它每一步有沒有多個Parent,都是回到第一個Parent。
在每一次新增或修改後,若想要將這些變動紀錄到數據庫中,需要commit上去。透過commit,數據庫會依時間記錄更動的內容等等。建議每個不同目的,例如:新增、修復... 都可以分開提交。
關於基本的數據庫設定與運用可以參考:
終端機 & Git 指令
前面我們大致上知道這些指令
git add
git commit
git status
(看看檔案庫內有幾個沒追蹤的檔案...等等)git log
git pull
那麼在和多人一起工作時,還會再有哪些常用的指令呢?
git clone 網址
若是使用SSH的話就需要另外做設定。
補充: 關於http和ssh兩種協議
git branch
分支預設的branch就是master(main),而若是多人合作,同時有人修改、有人添加新功能...,全部都在master上作業的話,應該就會大亂了吧!而且master通常會是正式版資料,為了不影響正式版,我們需要透過再開分支來測試或開發。
branch 就是指向某個 commit 的 reference, branch 其實就是在說:「我想要包含這一次的 commit 以及它的所有 parent 的 commit。」
git branch 分支名稱
git branch
git checkout 分支名稱
git branch -d 分支名稱
(-D 是強制刪除)git reset HEAD^
git merge 分支名稱
rebasing 就是取出一連串的 commit,"複製"它們,然後把它們接在別的地方。
不同於merge的地方是,rebasing比較線性,也可以更明確看到修改歷程。
假設有一個feature分支 與 main分支
我要將feature和main合併了,可以將branch移到feature分支上然後輸入git rebase bugFix
通常預設分支就是 master
開發分支 develop
開發新功能分支 feature (feature/01、feature/xxx)
首先要先了解 HEAD。
HEAD 是一個指標,指向某一個分支,通常你可以把 HEAD 當做「目前所在分支」看待。
假如說我有三次Commit,現在我的HEAD應該會在我的第三次Commit上,如果我想要回去看我的第一次Commit,我可以輸入git checkout 第一次Commit的編號
或 git checkout HEAD~2
(回兩步)。這時候我的HEAD就會指向在第一次Commit的地方。
HEAD 預設會一直跟著 master/main分支。
學習自: youtube channel 高見龍
只 Push 部份的進度是什麼意思呢? 假設今天你想每日上傳一直影片,為了保險起見,你可能預先多拍了好幾隻。今天只是Day4,但你已經拍到Day8。
如果直接使用git push origin master
,會將全部都推上去。
所以要怎麼只推Day4的就好了?
首先可以要先理解一下 git push origin master
的概念。
這個指令的完整版其實是: git push origin master: master
解構來看: git推送master這個分支到origin節點,並產生新的master分支。
所以:git push origin master: new
就是:git推送master這個分支到origin節點,並產生新的new分支。
Ok! 那麼如果只要推Day4上去呢?
git push origin Day4: master
or git push origin Day4
這個 Day4 其實會是Commit的編號
這兩個符號都是幫助我們在reset或是checkout時,作相對位置的移動。
範例:
git checkout HEAD^
:我要將現在的HEAD移動到(現在HEAD的)前一個地方git checkout HEAD^^
:我要將現在的HEAD移動到(現在HEAD的)前兩步地方git checkout HEAD~3
:我要將現在的HEAD移動到(現在HEAD的)前三步地方git checkout HEAD^^^
但是這兩者上又有些不同。假設現在這個HEAD它是合併來的,它有兩個parent node。這樣他的上一步要回去哪個呢?
如果是使用git checkout HEAD^
,它會回到第一個Parent,因為它的完整其實是git checkout HEAD^1
。
如果要回到第二個Parent呢? 要輸入git checkout HEAD^2
要特別注意是跟git checkout HEAD~2
不一樣喲!
git checkout HEAD~2
是回兩步,但是不管它每一步有沒有多個Parent,都是回到第一個Parent。
Git 有四種協議用來傳輸數據。
在多人合作上,我們通常會用到的就是後面那兩種。
雖然使用HTTPS來複製URL是比較簡單的。
想要將遠端的專案複製下來只需要在終端機輸入:
git clone HTTPS網址
但是想要抓取(fetch)或推到(push)遠端時,就需要在輸入帳號密碼,如果不需要大多是帳密(Key Chain)已存在電腦內。
所以HTTPS通常會用在想要複製別人的專案來操作,但不需要再推回去時。
使用SSH來複製URL就需要先設置 SSH key(金鑰)。
雖然需要先在電腦內設定好金鑰,但再上傳時就不需要再輸入額外帳密。
在多人合作時,就會很方便。
首先的首先,打開終端機!!
輸入指令:
$ ssh-keygen -t ed25519 -C "your_email@example.com"
如果你的電腦系統不支援Ed25519 algorithm,要輸入下面這行
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
接下來會看到這兩行:
Generating public/private ed25519 key pair.
Enter file in which to save the key (....):
直接按enter就好
...overwrite(y/n)
Enter passphrase (empty for no passphrase): [Type a passphrase]
Enter same passphrase again: [Type passphrase again]
輸入兩次密碼(自訂),你的金鑰就會生成囉!什麼是金鑰代理?
啟用SSH的代理伺服器,是為了讓我們之後使用這個金鑰時,不用再額外輸入密碼
$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_ed25519
。(在這裡你會需要輸入密碼呦)(這裡是使用網頁版的GitHub.com)
接下來如果你的名稱與id_ed25519不同,都要記得自己修改掉呦。
ok!現在你到電腦的/c/Users/user/地方應該就會看到 .ssh
檔案,打開就會看到金鑰的名稱檔案,有的話就可以繼續下去!
clip < ~/.ssh/id_ed25519.pub
settings
,再進入到 SSH and GPG keys
,點擊 New SSH key
或 Add SSH key
上面這樣就完成啦! 接下來你可以進行一次git push
,確認是否成功囉!
Git 有四種協議用來傳輸數據。
在多人合作上,我們通常會用到的就是後面那兩種。
雖然使用HTTPS來複製URL是比較簡單的。
想要將遠端的專案複製下來只需要在終端機輸入:
git clone HTTPS網址
但是想要抓取(fetch)或推到(push)遠端時,就需要在輸入帳號密碼,如果不需要大多是帳密(Key Chain)已存在電腦內。
所以HTTPS通常會用在想要複製別人的專案來操作,但不需要再推回去時。
使用SSH來複製URL就需要先設置 SSH key(金鑰)。
雖然需要先在電腦內設定好金鑰,但再上傳時就不需要再輸入額外帳密。
在多人合作時,就會很方便。
首先的首先,打開終端機!!
輸入指令:
$ ssh-keygen -t ed25519 -C "your_email@example.com"
如果你的電腦系統不支援Ed25519 algorithm,要輸入下面這行
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
接下來會看到這兩行:
Generating public/private ed25519 key pair.
Enter file in which to save the key (....):
直接按enter就好
...overwrite(y/n)
Enter passphrase (empty for no passphrase): [Type a passphrase]
Enter same passphrase again: [Type passphrase again]
輸入兩次密碼(自訂),你的金鑰就會生成囉!什麼是金鑰代理?
啟用SSH的代理伺服器,是為了讓我們之後使用這個金鑰時,不用再額外輸入密碼
$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_ed25519
。(在這裡你會需要輸入密碼呦)(這裡是使用網頁版的GitHub.com)
接下來如果你的名稱與id_ed25519不同,都要記得自己修改掉呦。
ok!現在你到電腦的/c/Users/user/地方應該就會看到 .ssh
檔案,打開就會看到金鑰的名稱檔案,有的話就可以繼續下去!
clip < ~/.ssh/id_ed25519.pub
settings
,再進入到 SSH and GPG keys
,點擊 New SSH key
或 Add SSH key
上面這樣就完成啦! 接下來你可以進行一次git push
,確認是否成功囉!
| Windows | MacOS | 說明 |
| cd | cd | 前往資料夾路徑 |
| pwd | pwd |取得目前所在的位置|
| mkdir | mkdir |新增資料夾|
| touch | touch |開新檔案|
| copy | cp |複製檔案|
| move | mv |移動檔案|
| del | rm |刪除檔案|
| cls | clear |清除畫面上的內容|
1.初始化數據庫: git init
2.查詢當前狀態:git status
3.將檔案加入到索引:git add .
4.將索引檔案變成一個更新(commit):git commit -m "新增網頁環境"
5.觀察 commit 歷史紀錄: git log
6.下載遠端數據庫: git clone 數據庫網址
7.更新遠端數據庫: git push origin master
8.強制更新遠端數據庫: git push -f
| Windows | MacOS | 說明 |
| cd | cd | 前往資料夾路徑 |
| pwd | pwd |取得目前所在的位置|
| mkdir | mkdir |新增資料夾|
| touch | touch |開新檔案|
| copy | cp |複製檔案|
| move | mv |移動檔案|
| del | rm |刪除檔案|
| cls | clear |清除畫面上的內容|
1.初始化數據庫: git init
2.查詢當前狀態:git status
3.將檔案加入到索引:git add .
4.將索引檔案變成一個更新(commit):git commit -m "新增網頁環境"
5.觀察 commit 歷史紀錄: git log
6.下載遠端數據庫: git clone 數據庫網址
7.更新遠端數據庫: git push origin master
8.強制更新遠端數據庫: git push -f
在示範中的最初始樣子,有個一個class 為 columns 的 div。
沒有CSS時的樣子
首先當使用了 display: grid
會發現好像沒什麼改變
要讓grid作三欄排列,還要加上grid-template-columns
撰寫方式:
.columns{
display: grid;
grid-template-columns: 33% 33% 33%
}
讓他每一欄為33%均分。
當然也可以變成grid-template-columns: 50% 25% 25%
感覺起來Grid比起Flex好像可以更精準、更簡單的控制排版。
但接下來就遇到我們第一個問題!
如果我把上面三欄扣掉一欄呢?
Grid好像沒有辦法像Flex一樣,幫我們自動修改讓兩個都變成 50%。
其實是可以的!
.columns{
display: grid;
grid-auto-flow: column
}
換成加上grid-auto-flow: column
,你會發現不管增加欄位或是減少欄位,Grid都會當我們自動均分。
如果想要讓欄位間都有固定的間隔,可以加上gap: ?em/px/...
.columns{
display: grid;
grid-auto-flow: column;
gap: 2em;
}
可以將columns 換成row 看看不同的地方。
現在的情境是,在平板以下時變成單欄,大於平板時變回均分欄位(三欄)
.columns{
display: grid;
grid-auto-flow: row;
gap: 2em;
}
@media (min-width: 767px){
.columns{
grid-auto-flow: column;
}
練習到這裡,熟習flexbox的人應該就可以感覺到不同了! 其他的地方不說,光是看row和column的角度就不一樣。
在flexbox中,如果你要多個東西一起排在同一行(水平排放)是flex-direction: row
;如果需要多個東西垂直排放是flex-direction: column
,這樣的作法可以理解flexbox的思考角度是,從父層看子層,父層要讓內部子層的東西排放的方向。
而在grid中,如果你要多個東西一起排在同一行(水平排放)是grid-auto-flow: column
;如果需要多個東西垂直排放是grid-auto-flow: row
,這樣的作法可以理解grid的思考角度是,整個畫面感,如果要呈現多個東西放水平那他的畫面感就會是一欄一欄,而多個東西要垂直放,他的畫面感就會是很多一行一行。
這是我自己的理解方法啦! 有寫人會說grid這樣比較直觀,只要想你的畫面要怎麼呈現就好,但是因為我用了flexbox很久,所以兩個對我來說都算可以理解。
<div class="container" style="border: solid 2px black; width: 300px; height: 300px">
<div class="box" style="background-color: blue"></div>
</div>
Grid 可以把它想成一個網格系統
你可以先在外層設定他的網格系統,例如:
//這個網格系統是5*5的均分格式
.container {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}
這樣的網格也有一個比較簡寫的寫法就是repeat(數量,值)
//這個網格系統是5*5的均分格式
.container {
display: grid;
grid-template-columns: repeat(5,20%);
grid-template-rows: repeat(5,20%);
}
還有另一種寫法fr
,fr
的運用就像式幾分之幾一樣,假如說今天要分兩個欄為,一個欄位是3分之1,另一個就是3分之2。就可以這樣寫
.container {
display: grid;
grid-template-columns: 1fr 2fr;
grid-template-rows: repeat(5,20%);
}
另外,網格系統不限於只用%
,也可以使用em
,px
等等,並且可以混用喔!
例如grid-template-columns: 100px 3em 40%
//讓東西放在grid第三欄第三行
.box{
grid-column: 3;
grid-row: 3;
.box{
grid-column: 2/5;
grid-row: 3/4;
}
其實詳細的寫法應該為:
.box{
grid-column-start: 2;
grid-column-end: 5;
grid-row-start: 3;
grid-row-end: 4
}
也還有更加簡化的寫法:
grid-area: grid-row-start/grid-column-start/grid-row-end/grid-column-end
.box{
grid-area: 3/2/4/5
}
應該不難理解!
除了明確寫出要讓內層出現在第幾格結束於第幾格,也可以使用order
。
每一個內層物件其實有預設order: 0
,order
有點像是z-index
一樣,可以為正或負數。
我們在剛剛的container內再加一個<div class="box2" style="background-color: pink"></div>
//預設都是 order: 0
.box{
order: 0
}
如果像讓兩個順序對調呢?
.box2{
order: -1
}
為甚麼是-1呢? 因為所有元素的order預設都是0,要依照他本來的排序和預設的0來決定,要修改排序時需要的值。
下面這個小遊戲幫助我很多!
練習Grid小遊戲
在示範中的最初始樣子,有個一個class 為 columns 的 div。
沒有CSS時的樣子
首先當使用了 display: grid
會發現好像沒什麼改變
要讓grid作三欄排列,還要加上grid-template-columns
撰寫方式:
.columns{
display: grid;
grid-template-columns: 33% 33% 33%
}
讓他每一欄為33%均分。
當然也可以變成grid-template-columns: 50% 25% 25%
感覺起來Grid比起Flex好像可以更精準、更簡單的控制排版。
但接下來就遇到我們第一個問題!
如果我把上面三欄扣掉一欄呢?
Grid好像沒有辦法像Flex一樣,幫我們自動修改讓兩個都變成 50%。
其實是可以的!
.columns{
display: grid;
grid-auto-flow: column
}
換成加上grid-auto-flow: column
,你會發現不管增加欄位或是減少欄位,Grid都會當我們自動均分。
如果想要讓欄位間都有固定的間隔,可以加上gap: ?em/px/...
.columns{
display: grid;
grid-auto-flow: column;
gap: 2em;
}
可以將columns 換成row 看看不同的地方。
現在的情境是,在平板以下時變成單欄,大於平板時變回均分欄位(三欄)
.columns{
display: grid;
grid-auto-flow: row;
gap: 2em;
}
@media (min-width: 767px){
.columns{
grid-auto-flow: column;
}
練習到這裡,熟習flexbox的人應該就可以感覺到不同了! 其他的地方不說,光是看row和column的角度就不一樣。
在flexbox中,如果你要多個東西一起排在同一行(水平排放)是flex-direction: row
;如果需要多個東西垂直排放是flex-direction: column
,這樣的作法可以理解flexbox的思考角度是,從父層看子層,父層要讓內部子層的東西排放的方向。
而在grid中,如果你要多個東西一起排在同一行(水平排放)是grid-auto-flow: column
;如果需要多個東西垂直排放是grid-auto-flow: row
,這樣的作法可以理解grid的思考角度是,整個畫面感,如果要呈現多個東西放水平那他的畫面感就會是一欄一欄,而多個東西要垂直放,他的畫面感就會是很多一行一行。
這是我自己的理解方法啦! 有寫人會說grid這樣比較直觀,只要想你的畫面要怎麼呈現就好,但是因為我用了flexbox很久,所以兩個對我來說都算可以理解。
<div class="container" style="border: solid 2px black; width: 300px; height: 300px">
<div class="box" style="background-color: blue"></div>
</div>
Grid 可以把它想成一個網格系統
你可以先在外層設定他的網格系統,例如:
//這個網格系統是5*5的均分格式
.container {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}
這樣的網格也有一個比較簡寫的寫法就是repeat(數量,值)
//這個網格系統是5*5的均分格式
.container {
display: grid;
grid-template-columns: repeat(5,20%);
grid-template-rows: repeat(5,20%);
}
還有另一種寫法fr
,fr
的運用就像式幾分之幾一樣,假如說今天要分兩個欄為,一個欄位是3分之1,另一個就是3分之2。就可以這樣寫
.container {
display: grid;
grid-template-columns: 1fr 2fr;
grid-template-rows: repeat(5,20%);
}
另外,網格系統不限於只用%
,也可以使用em
,px
等等,並且可以混用喔!
例如grid-template-columns: 100px 3em 40%
//讓東西放在grid第三欄第三行
.box{
grid-column: 3;
grid-row: 3;
.box{
grid-column: 2/5;
grid-row: 3/4;
}
其實詳細的寫法應該為:
.box{
grid-column-start: 2;
grid-column-end: 5;
grid-row-start: 3;
grid-row-end: 4
}
也還有更加簡化的寫法:
grid-area: grid-row-start/grid-column-start/grid-row-end/grid-column-end
.box{
grid-area: 3/2/4/5
}
應該不難理解!
除了明確寫出要讓內層出現在第幾格結束於第幾格,也可以使用order
。
每一個內層物件其實有預設order: 0
,order
有點像是z-index
一樣,可以為正或負數。
我們在剛剛的container內再加一個<div class="box2" style="background-color: pink"></div>
//預設都是 order: 0
.box{
order: 0
}
如果像讓兩個順序對調呢?
.box2{
order: -1
}
為甚麼是-1呢? 因為所有元素的order預設都是0,要依照他本來的排序和預設的0來決定,要修改排序時需要的值。
下面這個小遊戲幫助我很多!
練習Grid小遊戲
所有功能依據程式邏輯進行拆分
在 Vue3起手式 這篇文章中,使用的方法式Options API。
Options API 通過定義 data
,methods
,computed
,watch
等屬性與方法,共同處裡頁面邏輯。
若是資料就寫在data
屬性上;若是方法就寫在methods
上等,依這種方式來編寫。這樣編寫的缺點就是,若元件變得複雜時,對應的屬性列表可能也會增長,這樣會讓元件變得很難閱讀和理解。
因此 Composition API 就被設計出來解決問題啦!
Composition API 不會將功能分開依照屬性分類(也就是說不再需要寫data、methods區塊),所有內容寫在setup()中,也就可以用功能來區分寫的區塊,甚至可以把function抽出來放在setup外面,再放到setup裡面組起來,但若有需要使用再template的方法或資料,需要用return傳出來才可以使用。
簡易圖示表達:
若是一個大型元件,內部有很多的處理邏輯關注點,那麼看起來就會像這樣十分碎片化,很難維護與理解。當我們需要處理某個點時,必須要不斷地跳轉程式碼。
Composition API 將相關的程式碼都放在同一個函式中,這樣就解決的難維護與理解的問題。
//mainJS
import {createApp, ref, reactive, onMounted} from Vue
我們使用在OptionAPI的data``methods``mounted
在Composition API中全部放在setup()。
資料需要宣告變數放入setup並透過return回傳出來,才能渲染到畫面上
關於Composition API有一點非常重要是,資料一定要使用特定方法定義才行。。
定義資料 : ref
和reactive
。
定義方法 : 在setup內直接將函式寫出(一樣要return回來)。
生命週期 : onMounted等。
另外還有 computed,watch
//app.vue
<script>
...
export default {
setup() {
const text = ref('Hello')
const person = reactive({
name: 'Ted'
})
onMounted( ()=>{ getText() } );
function getText(){ console.log(text.value) };
onMounted( ()=>{ getText() } );
// computed
const num = ref(1);
//因為適用ref記得要用.value
const dbNum = computed((num)=>{ return num.value * 2 })
return{
text,
person,
getText,
num,
dbNum
}
}
};
</script>
ref
和reactive
的差別reactive
const text = reactive('錯誤方式') //這個會出錯(value cannot be made reactive:錯誤方式 )
const text = reactive( { name: '正確方式' })
ref
相對來說就沒有那麼多限制喲!
const text = ref('Bob');
const text2 = ref({ name: 'Ted' })
ref
內存取任何值,一定要加上.value
{{text2.value.name}}
這個的好處是在於,他不會覆蓋到實體。所有功能依據程式邏輯進行拆分
在 Vue3起手式 這篇文章中,使用的方法式Options API。
Options API 通過定義 data
,methods
,computed
,watch
等屬性與方法,共同處裡頁面邏輯。
若是資料就寫在data
屬性上;若是方法就寫在methods
上等,依這種方式來編寫。這樣編寫的缺點就是,若元件變得複雜時,對應的屬性列表可能也會增長,這樣會讓元件變得很難閱讀和理解。
因此 Composition API 就被設計出來解決問題啦!
Composition API 不會將功能分開依照屬性分類(也就是說不再需要寫data、methods區塊),所有內容寫在setup()中,也就可以用功能來區分寫的區塊,甚至可以把function抽出來放在setup外面,再放到setup裡面組起來,但若有需要使用再template的方法或資料,需要用return傳出來才可以使用。
簡易圖示表達:
若是一個大型元件,內部有很多的處理邏輯關注點,那麼看起來就會像這樣十分碎片化,很難維護與理解。當我們需要處理某個點時,必須要不斷地跳轉程式碼。
Composition API 將相關的程式碼都放在同一個函式中,這樣就解決的難維護與理解的問題。
//mainJS
import {createApp, ref, reactive, onMounted} from Vue
我們使用在OptionAPI的data``methods``mounted
在Composition API中全部放在setup()。
資料需要宣告變數放入setup並透過return回傳出來,才能渲染到畫面上
關於Composition API有一點非常重要是,資料一定要使用特定方法定義才行。。
定義資料 : ref
和reactive
。
定義方法 : 在setup內直接將函式寫出(一樣要return回來)。
生命週期 : onMounted等。
另外還有 computed,watch
//app.vue
<script>
...
export default {
setup() {
const text = ref('Hello')
const person = reactive({
name: 'Ted'
})
onMounted( ()=>{ getText() } );
function getText(){ console.log(text.value) };
onMounted( ()=>{ getText() } );
// computed
const num = ref(1);
//因為適用ref記得要用.value
const dbNum = computed((num)=>{ return num.value * 2 })
return{
text,
person,
getText,
num,
dbNum
}
}
};
</script>
ref
和reactive
的差別reactive
const text = reactive('錯誤方式') //這個會出錯(value cannot be made reactive:錯誤方式 )
const text = reactive( { name: '正確方式' })
ref
相對來說就沒有那麼多限制喲!
const text = ref('Bob');
const text2 = ref({ name: 'Ted' })
ref
內存取任何值,一定要加上.value
{{text2.value.name}}
這個的好處是在於,他不會覆蓋到實體。本篇文章從 TechBridge學習並參考。這裡只是自己的學習筆記喲!
ESM 全名是 「ES6 Modules or JavaScript Modules」。
為了因應 JavaScript 越來越多工、越寫越複雜,開發者覺得要將 JavaScript 模組化 才行阿!
假設今天有一個網頁,有好幾個相同的頁面都會渲染畫面的資料,檔案方別為 app1.js 與 app2.js,在沒有模組化前遇到這個狀況,就必須在不同檔案間重複撰寫。
模組化讓我們將有重複性且可以一直利用的程式碼片段抽出來當作模組使用。
說到模組可以先來了解一下Node.js。
Node.js裡面可以使用require
把內建的模組引入,同時可以使用module.exports
自己做一個模組輸出。
範例: 在utils.js
內有一個很常會用到的計算函式:
// utils.js
function calculate(n) {
return (n * 0.88) + 5 // 計算價格公式
}
module.exports = calculate // 把這個函式 export 出去
到另一個檔案app.js
就可以使用import引入:
// app.js
var calculate = require('./utils')
console.log(calculate(100))
除了函式可以輸出引入,物件也可以:
// utils.js
function calculate(n) {
return (n * 0.88) + 5 // 計算價格公式
}
module.exports = { cal: calculate}, name: 'money'}
// app.js
var obj = require('./utils')
console.log(obj.cal(100));
console.log(obj.name)
記得一個最簡單的觀念: module.exports 出什麼,import進來就會是一樣的東西!
Node.js 最基本的模組使用概念: module.exports 導出, require 引入。
等等怎麼橫空插出一個 CommonJS ?
在 ESM 成熟前,流傳著不同的模組化撰寫標準,而CommonJS就是其中一種。
CommonJS的標準: module.exports 導出, require 引入。
後來這個標準就被Node.js
採用,所以上面Node.js
的模組基本概念就是從CommonJS
來的
好的,那上面提到Node.js
基於CommonJS
有了模組標準,但是這樣的寫法瀏覽器原生不支援這個東西(也就是瀏覽器不能用 module.exports 導出, require 引入)。
但是!!!瀏覽器可以借助其他工具來達成目的。
在2011年時,browserify出現了! browserify 是一個可以讓你在瀏覽器上使用reauire
的指令。
指令npx browserify /*entry point*/ -o /*output*/
可以在終端機上使用指令 npx browserify main.js -o bundle.js
,來打包main.js 與 utils.js。
在bundle.js內,把的程式碼用一個 function 包住,提供一個叫做 require 的 function 以及一個叫做 module 的物件來使用」,
下次再來好好探討browserify的運作!
那除了 browserify 外,還有一個在前端更有名的 webpack。
webpack和browserify 其實是相似的! browserify 使用指令來指定Entry point 與 Output。
而webpack是將這些寫成一個設定檔案:webpack.config.js
module.exports = {
mode: 'development' //開發模式(另外還有生產模式 production)
entry: './main.js', //入口點
output: {
path: __dirname, //輸出路徑
filename: 'webpack_bundle.js' //輸出的檔案名稱
}
}
接著在終端機輸入指令來安裝並執行
npm init -y
npm install webpack webpack-cli --save-dev
npx webpack --config webpack.config.js
Webpack的整體作法與 browserify 類似,那為什麼要使用webpack?因為要在瀏覽器上面使用 CommonJS 的模組機制,就必須使用工具先把程式碼打包才能做到
ok! 現在我們知道 webpack 幫助我們打包程式碼然後讓JS模組化並可以使用require
,那ES6中的import
與export
呢?
前面有提到在 ES6 出現以前,JavaScript 並沒有一個標準的模組化規範。Node.js 支援 CommonJS,所以才可以用require跟module.exports,但是瀏覽器原生沒有支援,所以才需要像是 browserify 以及 webpack 這種工具。
而 ES6 出來之後,有了正式的規範( import 與 export ),我們可以把之前的 main.js 與 utils.js 改成 import 與 export 的形式:
// main.js
import obj from './utils'
console.log(obj.cal(30))
console.log(obj.name)
//utils.js
function calculate(n) {
return ((n * 100 + 20 - 4)) % 10 + 3 // 計算價格公式
}
export default {
cal: calculate,
name: 'hello'
}
看起來好像很好~但是真的要只使用ES6 的 import 與 export其實是滿麻煩的!
type="module"
file:///
)。必須使用伺服器的方式開啟才行(在同一個目錄底下輸入指令(python -m SimpleHTTPServer 8080
,接著就可以打開:http://localhost:8080
。)./utilis
,要寫./utilis.js
)import pad from './node_modules/pad-left/index.js'
這樣的方式引入嗎?QQ 這樣的寫法維護性相當差,若是模組的入口點變了,你就必須改寫所有 import 的地方。而且難道要把整個 node_modules 資料夾一起傳上去嗎?主要只使用 ESM 的問題就是相容性、無法兼容 npm 等等,所以我們就要再回來看看 WEBPACK 啦!
前面提到設定一個路徑檔案webpack.config.js
module.exports = {
mode: 'development' //開發模式(另外還有生產模式 production)
entry: './main.js', //入口點
output: {
path: __dirname, //輸出路徑
filename: 'webpack_bundle.js' //輸出的檔案名稱
}
}
接著在終端機輸入指令來安裝並執行
npm init -y
npm install webpack webpack-cli --save-dev
npx webpack --config webpack.config.js
我們可以試著載入一個套件
npm install pad-left
並在 main.js 裡面引入套件並且使用:
import obj from './utils.js'
import pad from 'pad-left'
console.log(obj.cal(100))
console.log(pad('4', 4, 0))
然後一樣按照之前教過的流程打包檔案,config 檔之前已經寫過了,所以直接下指令就好:
npx webpack --config webpack.config.js
接著打開 index.html,更改引入的 script,因為有 webpack 的關係所以不需要 type=module
了:
<html>
<head>
</head>
<body>
<script src="./webpack_bundle.js"></script>
</body>
</html>
最後打開 index.html,看到 console 上面出現 9 跟 0004 這兩個輸出,就代表有打包成功了。
使用 webpack 的好處之一,就是我們能把使用 npm 安裝的模組一併打包,就跟打包自己寫的模組一樣,不需要多做其他事情。這件事是原生的瀏覽器沒辦法做到的。
webpack除了將JS打包模組化外,也將範圍延伸到像CSS或是IMG,任何資源都可以 import 進來使用。為了要支援這樣的功能,webpack 定義了許多 loader(載入器),不同的資源有不同的 loader,要透過 loader 處理才能把資源載入。而這個 loader 也是它強大的地方。例如說你可以用 scss loader 載入 scss 的檔案,在引入資源的時候就會幫你順便編譯成 CSS,所以你不用自己做這件事。JS 也是一樣,你可以寫最新最潮的語法,然後在載入時用 babel-loader 把 ES10 的語法轉成 ES5。
Q:為什麼很多專案(例如說 React)在部署前都要先 build?這個步驟在幹嘛你知道嗎?
因為原始碼沒辦法直接放上去瀏覽器(會沒有辦法執行),所以一定要經過 webpack 打包處理,打包完的檔案才能讓瀏覽器執行。
Q:你知道 require/module.exports 與 import/export 的差別嗎?
require/module.exports 是一套叫做 CommonJS 的規範,Node.js 有支援,瀏覽器沒有。
import/export 是 ES6 的規範,Node.js 部分支援,瀏覽器也是部分支援。
Q:你知道 import 與 export 這兩個語法在瀏覽器上面不是隨便就能使用嗎?
有使用限制,例如說要加上 type=module,而且也沒辦法直接引入 npm 裡的模組,要把路徑寫死才能使用。而瀏覽器支援度也是一個考量,IE11 並不支援此種寫法。
Q:你知道為什麼要用 webpack 嗎?
原因有很多,例如說:
我想使用 npm 上的第三方模組
我想把圖片當作資源 import 進來
我想把 CSS 當作資源 import 進來
我想在一個地方就處理好 uglify 與 minify
不過重點其實是第一個,因為瀏覽器原生的 ES6 模組支援度沒那麼高,尤其是引入第三方模組,所以才需要透過 webpack 或其他打包工具幫我們處理好這一段。
Q:你知道 webpack 為什麼要有 loader 嗎?
因為把圖片或是 CSS 當作資源引入這並不是正式的規範,而是 webpack 自己延伸的定義。為了支援這些資源,就必須特別寫一個 loader 去載入,否則預設的 loader 只能載入 JavaScript。
本篇文章從 TechBridge學習並參考。這裡只是自己的學習筆記喲!
ESM 全名是 「ES6 Modules or JavaScript Modules」。
為了因應 JavaScript 越來越多工、越寫越複雜,開發者覺得要將 JavaScript 模組化 才行阿!
假設今天有一個網頁,有好幾個相同的頁面都會渲染畫面的資料,檔案方別為 app1.js 與 app2.js,在沒有模組化前遇到這個狀況,就必須在不同檔案間重複撰寫。
模組化讓我們將有重複性且可以一直利用的程式碼片段抽出來當作模組使用。
說到模組可以先來了解一下Node.js。
Node.js裡面可以使用require
把內建的模組引入,同時可以使用module.exports
自己做一個模組輸出。
範例: 在utils.js
內有一個很常會用到的計算函式:
// utils.js
function calculate(n) {
return (n * 0.88) + 5 // 計算價格公式
}
module.exports = calculate // 把這個函式 export 出去
到另一個檔案app.js
就可以使用import引入:
// app.js
var calculate = require('./utils')
console.log(calculate(100))
除了函式可以輸出引入,物件也可以:
// utils.js
function calculate(n) {
return (n * 0.88) + 5 // 計算價格公式
}
module.exports = { cal: calculate}, name: 'money'}
// app.js
var obj = require('./utils')
console.log(obj.cal(100));
console.log(obj.name)
記得一個最簡單的觀念: module.exports 出什麼,import進來就會是一樣的東西!
Node.js 最基本的模組使用概念: module.exports 導出, require 引入。
等等怎麼橫空插出一個 CommonJS ?
在 ESM 成熟前,流傳著不同的模組化撰寫標準,而CommonJS就是其中一種。
CommonJS的標準: module.exports 導出, require 引入。
後來這個標準就被Node.js
採用,所以上面Node.js
的模組基本概念就是從CommonJS
來的
好的,那上面提到Node.js
基於CommonJS
有了模組標準,但是這樣的寫法瀏覽器原生不支援這個東西(也就是瀏覽器不能用 module.exports 導出, require 引入)。
但是!!!瀏覽器可以借助其他工具來達成目的。
在2011年時,browserify出現了! browserify 是一個可以讓你在瀏覽器上使用reauire
的指令。
指令npx browserify /*entry point*/ -o /*output*/
可以在終端機上使用指令 npx browserify main.js -o bundle.js
,來打包main.js 與 utils.js。
在bundle.js內,把的程式碼用一個 function 包住,提供一個叫做 require 的 function 以及一個叫做 module 的物件來使用」,
下次再來好好探討browserify的運作!
那除了 browserify 外,還有一個在前端更有名的 webpack。
webpack和browserify 其實是相似的! browserify 使用指令來指定Entry point 與 Output。
而webpack是將這些寫成一個設定檔案:webpack.config.js
module.exports = {
mode: 'development' //開發模式(另外還有生產模式 production)
entry: './main.js', //入口點
output: {
path: __dirname, //輸出路徑
filename: 'webpack_bundle.js' //輸出的檔案名稱
}
}
接著在終端機輸入指令來安裝並執行
npm init -y
npm install webpack webpack-cli --save-dev
npx webpack --config webpack.config.js
Webpack的整體作法與 browserify 類似,那為什麼要使用webpack?因為要在瀏覽器上面使用 CommonJS 的模組機制,就必須使用工具先把程式碼打包才能做到
ok! 現在我們知道 webpack 幫助我們打包程式碼然後讓JS模組化並可以使用require
,那ES6中的import
與export
呢?
前面有提到在 ES6 出現以前,JavaScript 並沒有一個標準的模組化規範。Node.js 支援 CommonJS,所以才可以用require跟module.exports,但是瀏覽器原生沒有支援,所以才需要像是 browserify 以及 webpack 這種工具。
而 ES6 出來之後,有了正式的規範( import 與 export ),我們可以把之前的 main.js 與 utils.js 改成 import 與 export 的形式:
// main.js
import obj from './utils'
console.log(obj.cal(30))
console.log(obj.name)
//utils.js
function calculate(n) {
return ((n * 100 + 20 - 4)) % 10 + 3 // 計算價格公式
}
export default {
cal: calculate,
name: 'hello'
}
看起來好像很好~但是真的要只使用ES6 的 import 與 export其實是滿麻煩的!
type="module"
file:///
)。必須使用伺服器的方式開啟才行(在同一個目錄底下輸入指令(python -m SimpleHTTPServer 8080
,接著就可以打開:http://localhost:8080
。)./utilis
,要寫./utilis.js
)import pad from './node_modules/pad-left/index.js'
這樣的方式引入嗎?QQ 這樣的寫法維護性相當差,若是模組的入口點變了,你就必須改寫所有 import 的地方。而且難道要把整個 node_modules 資料夾一起傳上去嗎?主要只使用 ESM 的問題就是相容性、無法兼容 npm 等等,所以我們就要再回來看看 WEBPACK 啦!
前面提到設定一個路徑檔案webpack.config.js
module.exports = {
mode: 'development' //開發模式(另外還有生產模式 production)
entry: './main.js', //入口點
output: {
path: __dirname, //輸出路徑
filename: 'webpack_bundle.js' //輸出的檔案名稱
}
}
接著在終端機輸入指令來安裝並執行
npm init -y
npm install webpack webpack-cli --save-dev
npx webpack --config webpack.config.js
我們可以試著載入一個套件
npm install pad-left
並在 main.js 裡面引入套件並且使用:
import obj from './utils.js'
import pad from 'pad-left'
console.log(obj.cal(100))
console.log(pad('4', 4, 0))
然後一樣按照之前教過的流程打包檔案,config 檔之前已經寫過了,所以直接下指令就好:
npx webpack --config webpack.config.js
接著打開 index.html,更改引入的 script,因為有 webpack 的關係所以不需要 type=module
了:
<html>
<head>
</head>
<body>
<script src="./webpack_bundle.js"></script>
</body>
</html>
最後打開 index.html,看到 console 上面出現 9 跟 0004 這兩個輸出,就代表有打包成功了。
使用 webpack 的好處之一,就是我們能把使用 npm 安裝的模組一併打包,就跟打包自己寫的模組一樣,不需要多做其他事情。這件事是原生的瀏覽器沒辦法做到的。
webpack除了將JS打包模組化外,也將範圍延伸到像CSS或是IMG,任何資源都可以 import 進來使用。為了要支援這樣的功能,webpack 定義了許多 loader(載入器),不同的資源有不同的 loader,要透過 loader 處理才能把資源載入。而這個 loader 也是它強大的地方。例如說你可以用 scss loader 載入 scss 的檔案,在引入資源的時候就會幫你順便編譯成 CSS,所以你不用自己做這件事。JS 也是一樣,你可以寫最新最潮的語法,然後在載入時用 babel-loader 把 ES10 的語法轉成 ES5。
Q:為什麼很多專案(例如說 React)在部署前都要先 build?這個步驟在幹嘛你知道嗎?
因為原始碼沒辦法直接放上去瀏覽器(會沒有辦法執行),所以一定要經過 webpack 打包處理,打包完的檔案才能讓瀏覽器執行。
Q:你知道 require/module.exports 與 import/export 的差別嗎?
require/module.exports 是一套叫做 CommonJS 的規範,Node.js 有支援,瀏覽器沒有。
import/export 是 ES6 的規範,Node.js 部分支援,瀏覽器也是部分支援。
Q:你知道 import 與 export 這兩個語法在瀏覽器上面不是隨便就能使用嗎?
有使用限制,例如說要加上 type=module,而且也沒辦法直接引入 npm 裡的模組,要把路徑寫死才能使用。而瀏覽器支援度也是一個考量,IE11 並不支援此種寫法。
Q:你知道為什麼要用 webpack 嗎?
原因有很多,例如說:
我想使用 npm 上的第三方模組
我想把圖片當作資源 import 進來
我想把 CSS 當作資源 import 進來
我想在一個地方就處理好 uglify 與 minify
不過重點其實是第一個,因為瀏覽器原生的 ES6 模組支援度沒那麼高,尤其是引入第三方模組,所以才需要透過 webpack 或其他打包工具幫我們處理好這一段。
Q:你知道 webpack 為什麼要有 loader 嗎?
因為把圖片或是 CSS 當作資源引入這並不是正式的規範,而是 webpack 自己延伸的定義。為了支援這些資源,就必須特別寫一個 loader 去載入,否則預設的 loader 只能載入 JavaScript。
我們只需要安裝sass-loader
和node-sass
npm install -D sass-loader node-sass
將lang設為scss。
路徑:src/assets/scss
內App.vue
中引入<style lang="scss" scoped>
@import './assets/scss/main.scss';
</style>
or<style lang="scss" scoped src="./assets/scss/main.scss"></style>
main.js
引入import '@/assets/scss/main.scss';
只能說電腦程式一切一切都太神奇了!我真的覺得自己就是一個超級超級小的存在,學了一陣子大概也只了解了一百兆分之一吧!
這個方法是我網路上找到的解答:
npm uninstall node-sass
npm install sass-loader
npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
]]>我們只需要安裝sass-loader
和node-sass
npm install -D sass-loader node-sass
將lang設為scss。
路徑:src/assets/scss
內App.vue
中引入<style lang="scss" scoped>
@import './assets/scss/main.scss';
</style>
or<style lang="scss" scoped src="./assets/scss/main.scss"></style>
main.js
引入import '@/assets/scss/main.scss';
只能說電腦程式一切一切都太神奇了!我真的覺得自己就是一個超級超級小的存在,學了一陣子大概也只了解了一百兆分之一吧!
這個方法是我網路上找到的解答:
npm uninstall node-sass
npm install sass-loader
npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
]]>
modal彈跳視窗
因為觸發Model的按鈕通常會在內層,但是彈跳的視窗應該是要對應整個外層置中。
可以指定元件內某些區塊的傳送位置。
<body>
<div>
<modal-button></modal-button>
</div>
<body>
app.component('modal-button',{
template:`
<button @click="modal-open = true">
open full screen modal!
</button>
//下面是modal內容
//運用teleport指定位置
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
modal content
<button @click="modalOpen = false"> Close </button>
</div>
</div>
<teleport>
`,
data(){
return{
modalOpen: false
}
}
})
]]>modal彈跳視窗
因為觸發Model的按鈕通常會在內層,但是彈跳的視窗應該是要對應整個外層置中。
可以指定元件內某些區塊的傳送位置。
<body>
<div>
<modal-button></modal-button>
</div>
<body>
app.component('modal-button',{
template:`
<button @click="modal-open = true">
open full screen modal!
</button>
//下面是modal內容
//運用teleport指定位置
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
modal content
<button @click="modalOpen = false"> Close </button>
</div>
</div>
<teleport>
`,
data(){
return{
modalOpen: false
}
}
})
]]>
之前學習到的元件,我們知道元件可以重複使用並且運用props
傳入不同資訊。
而使用props
的有很嚴格的父層子層規範。子元件控制HTML內容,而父元件傳入不同的資料。
現在要認識的Slot跟這個概念相似但是又更好用了!Slots也可以讓我們重複運用元件,並直接用父元件控制子元件的內容。
Greet-Component
<template>
<div>
<slot>Default Hello<slot/>
</div>
<template>
<script>
export default{
name: 'greet'
}
</script>
App-component
<template>
<greet/>
<greet>你好!</greet>
</template>
<script>
import greet form './component/greet.vue(路徑)'
export default {
component: {greet}
}
</script>
card-Component
<template>
<div>
<slot name="header">Default header<slot/>
</div>
<div>
//可以留一個沒有命名的,就會試default
<slot>Default content<slot/>
</div>
<div>
<slot name="footer">Default footer<slot/>
</div>
<template>
<script>
export default{
name: 'card'
}
</script>
App-component
<template>
<card>
//若是命名的Slot,需要用 template。用v-slot指定名稱。
<template v-slot:header> CARD HEADER</template>
//其中沒有命名的slot,就會變default
<template v-slot:default>CARD BODY</template>
//若是命名的Slot,需要用 template。可以用 # 簡寫。
<template #footer>CARD FOOTER</template>
</card>
</template>
<script>
import card form './component/card.vue(路徑)'
export default {
component: {card}
}
</script>
Slot Props 可以藉由Slot除送資料到父元件。
範例練習:
namelist元件中有名單資要要傳除去,我們同時希望父元件利用Slot Props操作資料。
namelist-Component
<template>
<p v-for="name in names" :key="name.firstname">
<slot :firstname="name.firstname" :lastname="name.lastname"></slot>
</p>
<template>
<script>
export default{
name: 'namelist',
data(){
return{
names:[
{firstname: 'Bruce',lastname: 'Wayne'},
{firstname: 'Clark',lastname: 'Kent'},
]
}
}
}
</script>
App-component
<template>
<namelist>
<template v-slot:default="slotProps">
{{slotProps.firstname}} {{slotProps.lastname}}
</template>
<template v-slot:default="slotProps">
{{slotProps.firstname}}
</template>
</namelist>
</template>
<script>
import namelist form './component/namelist.vue(路徑)'
export default {
component: {namelist}
}
</script>
]]>之前學習到的元件,我們知道元件可以重複使用並且運用props
傳入不同資訊。
而使用props
的有很嚴格的父層子層規範。子元件控制HTML內容,而父元件傳入不同的資料。
現在要認識的Slot跟這個概念相似但是又更好用了!Slots也可以讓我們重複運用元件,並直接用父元件控制子元件的內容。
Greet-Component
<template>
<div>
<slot>Default Hello<slot/>
</div>
<template>
<script>
export default{
name: 'greet'
}
</script>
App-component
<template>
<greet/>
<greet>你好!</greet>
</template>
<script>
import greet form './component/greet.vue(路徑)'
export default {
component: {greet}
}
</script>
card-Component
<template>
<div>
<slot name="header">Default header<slot/>
</div>
<div>
//可以留一個沒有命名的,就會試default
<slot>Default content<slot/>
</div>
<div>
<slot name="footer">Default footer<slot/>
</div>
<template>
<script>
export default{
name: 'card'
}
</script>
App-component
<template>
<card>
//若是命名的Slot,需要用 template。用v-slot指定名稱。
<template v-slot:header> CARD HEADER</template>
//其中沒有命名的slot,就會變default
<template v-slot:default>CARD BODY</template>
//若是命名的Slot,需要用 template。可以用 # 簡寫。
<template #footer>CARD FOOTER</template>
</card>
</template>
<script>
import card form './component/card.vue(路徑)'
export default {
component: {card}
}
</script>
Slot Props 可以藉由Slot除送資料到父元件。
範例練習:
namelist元件中有名單資要要傳除去,我們同時希望父元件利用Slot Props操作資料。
namelist-Component
<template>
<p v-for="name in names" :key="name.firstname">
<slot :firstname="name.firstname" :lastname="name.lastname"></slot>
</p>
<template>
<script>
export default{
name: 'namelist',
data(){
return{
names:[
{firstname: 'Bruce',lastname: 'Wayne'},
{firstname: 'Clark',lastname: 'Kent'},
]
}
}
}
</script>
App-component
<template>
<namelist>
<template v-slot:default="slotProps">
{{slotProps.firstname}} {{slotProps.lastname}}
</template>
<template v-slot:default="slotProps">
{{slotProps.firstname}}
</template>
</namelist>
</template>
<script>
import namelist form './component/namelist.vue(路徑)'
export default {
component: {namelist}
}
</script>
]]>
上一篇文章有提到To create a production build, run npm run build.
。
在佈署一個專案前都會先輸入npm run build
,然後打包後的檔案就會出現在dist/
目錄下。
現在要來學習運用一個指令部署 Vue Cli 到 GitHub Pages。
總共有三個步驟
vue.config.js
deploy.sh
gitignore
vue.config.js
在 vue.config.js
中設置正確的 publicPath
。
有兩種情況:
https://<USERNAME>.github.io/
上, 那麼publicPath
將預設為/
。可以忽略這個參數。https://<USERNAME>.github.io/<REPO>
上,那麼就要將publicPath
設為"/<REPO>/"
範例: REPO => vueProject
publicPath
用來指定要部署的路徑。
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/vueProject/'
: '/'
}
deploy.sh
#!/usr/bin/env sh
# 當發生錯誤時終止
set -e
# 建構 (build這裡做的就是幫我們把Vue轉成HTML/JS/CSS,並放入dist)
npm run build
# save the lastest commit hash as a string
LOGSTRING=$(git log)
COMMIT=$(echo $LOGSTRING | awk '{print $2}')
# cd 到建構輸出的目錄下
cd dist
# 如果要部署到自定義域名
# echo 'www.example.com' > CNAME
git init
git add -A //加入整個dist
git commit -m "deploy (commit: $COMMIT)"
# 部署到 https://<USERNAME>.github.io
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git master
# 部署到 https://<USERNAME>.github.io/<REPO>
git push -f git@github.com:<USERNAME>/<REPO>.git master:gh-pages
cd ..
.gitignore
node_modules
dist
前情: 已經有了一個專案,然後要打包檔案放在dist/
目錄下。
vue-cli-service build
/ npm run build
它就會依照上面的指令操作囉!
上一篇文章有提到To create a production build, run npm run build.
。
在佈署一個專案前都會先輸入npm run build
,然後打包後的檔案就會出現在dist/
目錄下。
現在要來學習運用一個指令部署 Vue Cli 到 GitHub Pages。
總共有三個步驟
vue.config.js
deploy.sh
gitignore
vue.config.js
在 vue.config.js
中設置正確的 publicPath
。
有兩種情況:
https://<USERNAME>.github.io/
上, 那麼publicPath
將預設為/
。可以忽略這個參數。https://<USERNAME>.github.io/<REPO>
上,那麼就要將publicPath
設為"/<REPO>/"
範例: REPO => vueProject
publicPath
用來指定要部署的路徑。
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/vueProject/'
: '/'
}
deploy.sh
#!/usr/bin/env sh
# 當發生錯誤時終止
set -e
# 建構 (build這裡做的就是幫我們把Vue轉成HTML/JS/CSS,並放入dist)
npm run build
# save the lastest commit hash as a string
LOGSTRING=$(git log)
COMMIT=$(echo $LOGSTRING | awk '{print $2}')
# cd 到建構輸出的目錄下
cd dist
# 如果要部署到自定義域名
# echo 'www.example.com' > CNAME
git init
git add -A //加入整個dist
git commit -m "deploy (commit: $COMMIT)"
# 部署到 https://<USERNAME>.github.io
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git master
# 部署到 https://<USERNAME>.github.io/<REPO>
git push -f git@github.com:<USERNAME>/<REPO>.git master:gh-pages
cd ..
.gitignore
node_modules
dist
前情: 已經有了一個專案,然後要打包檔案放在dist/
目錄下。
vue-cli-service build
/ npm run build
它就會依照上面的指令操作囉!
Cli 就是一個下指令的工具。我們可以運用它去執行我們的專案。
主要分為三大方向:
打開終端機,輸入以下指令:
@next會安裝最新版本
npm install -g @vue/cli@next
//-g 將Cli安裝於全域
vue --version
//查看版本
這大概是最常用的指令了~
直接用Vue指令建立專案
vue create hello-world
輸入指令後,它會問你你想要建立哪個版本?
使用上下鍵選取並按enter確認,接下來它就會自動安裝一個基本的Vue專案下來啦!
接下來它就叫你前往Vue的資料夾,並執行專案
你應該會看到這個樣子
終端機:
資料夾結構:
cd hello-world
npm run serve
輸入後,它會跑出兩個網址:
接下來把 local 的網址貼上瀏覽器,就可以看到專案啦!
而最下方你會看到To create a production build, run npm run build.
這就是當你完成開發時,可以利用這裡的build
指令。
輸入後,Vue Cli 會透過@Vue/compiler-sfc
與共種loaders將SFC的.vue
檔案轉譯成瀏覽器看得懂的js檔案。
打包後的檔案會出現在dist/
目錄下
npm run
是一個小工具,點開package.json
,裡面你可以找到
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
我們輸入npm run
時,就是在跑這個script。
而 npm run serve
就是執行了 "serve": "vue-cli-service serve",
,
它提供一個小型的server,讓我們方便的去做簡單的測試。
vue-cli-service serve
的另一個優點是,hot module replacement
也就是可以即時更新。
相關文章: 運用 Cli 部署 Vue專案 到 GitHub Pages
使用指令開啟 Vue ui 介面
在Vue ui 上面,我們可以在這裡 新增 / 執行 / 管理 專案
vue ui
主要是 Vue ui 提供了一個網頁介面,讓我們可以調整專案(包括 Plugin/dependencies 等)
前面提到 Cli Service
幾個重要的指令 npm run serve
等。加速我們的開發,
那 Cli service 為什麼可以做到這些呢? 我們要來了解一下 Cli Service
的核心
Cli Service
的核心不了解webpack的,可以先轉讀這篇文章。
Webpack 5 筆記文章
Webpack 的基本概念很簡單,就是幫我們整理所有的檔案並轉譯成瀏覽器看得懂的程式碼。
Cli Service
就是把 webpack 當作自己的核心,所以 Vue cli 就有一些webpack的功能。
Vue cli 除了將 webpack 融入自己的核心,還做了一下 config 設定。
像是我們使用的指令Vue create
就是有運用到webpack核心,再配上適當的設定值,所以我們就可以用Vue create
建立一個初始化的Vue專案
Cli 就是一個下指令的工具。我們可以運用它去執行我們的專案。
主要分為三大方向:
打開終端機,輸入以下指令:
@next會安裝最新版本
npm install -g @vue/cli@next
//-g 將Cli安裝於全域
vue --version
//查看版本
這大概是最常用的指令了~
直接用Vue指令建立專案
vue create hello-world
輸入指令後,它會問你你想要建立哪個版本?
使用上下鍵選取並按enter確認,接下來它就會自動安裝一個基本的Vue專案下來啦!
接下來它就叫你前往Vue的資料夾,並執行專案
你應該會看到這個樣子
終端機:
資料夾結構:
cd hello-world
npm run serve
輸入後,它會跑出兩個網址:
接下來把 local 的網址貼上瀏覽器,就可以看到專案啦!
而最下方你會看到To create a production build, run npm run build.
這就是當你完成開發時,可以利用這裡的build
指令。
輸入後,Vue Cli 會透過@Vue/compiler-sfc
與共種loaders將SFC的.vue
檔案轉譯成瀏覽器看得懂的js檔案。
打包後的檔案會出現在dist/
目錄下
npm run
是一個小工具,點開package.json
,裡面你可以找到
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
我們輸入npm run
時,就是在跑這個script。
而 npm run serve
就是執行了 "serve": "vue-cli-service serve",
,
它提供一個小型的server,讓我們方便的去做簡單的測試。
vue-cli-service serve
的另一個優點是,hot module replacement
也就是可以即時更新。
相關文章: 運用 Cli 部署 Vue專案 到 GitHub Pages
使用指令開啟 Vue ui 介面
在Vue ui 上面,我們可以在這裡 新增 / 執行 / 管理 專案
vue ui
主要是 Vue ui 提供了一個網頁介面,讓我們可以調整專案(包括 Plugin/dependencies 等)
前面提到 Cli Service
幾個重要的指令 npm run serve
等。加速我們的開發,
那 Cli service 為什麼可以做到這些呢? 我們要來了解一下 Cli Service
的核心
Cli Service
的核心不了解webpack的,可以先轉讀這篇文章。
Webpack 5 筆記文章
Webpack 的基本概念很簡單,就是幫我們整理所有的檔案並轉譯成瀏覽器看得懂的程式碼。
Cli Service
就是把 webpack 當作自己的核心,所以 Vue cli 就有一些webpack的功能。
Vue cli 除了將 webpack 融入自己的核心,還做了一下 config 設定。
像是我們使用的指令Vue create
就是有運用到webpack核心,再配上適當的設定值,所以我們就可以用Vue create
建立一個初始化的Vue專案
背景環境
npm init
建立package.json
檔案https://gulpjs.com/plugins/ 點入網址就可以看到很多的plugin
在終端機載入
這個練習需要
pluging | purpose |
---|---|
gulp-sass | 編譯sass |
gulp-postcss cssnano | postcss 處理CSS prefix cssnano 優化程式碼 |
gulp-terser | 壓縮 JS |
browser-sync | 瀏覽器同步檢視 |
延伸:
gulp-postcss
: 以往都是使用 Sass 的 Compass 來加入 prefix,這種增加方式就是無差別的加入,但其實現在的 CSS 有 9 成以上的 prefix 都沒有加入的必要,所以就可以使用 postcss
。PostCSS 類似 Sass,但是他是直接編譯 .css
檔案,另外他的套件都是用插件的方式載入(所以像這次的練習,也另外載入了cssnano),並不是像 Sass 已經有固定的寫法。
npm install gulp-scss gulp-postcss cssnano gulp-terser browser-sync --save-dev
當然首先要新增gulpfile.js
接下來就是將我們所有的套件載入
//gulpfile.js
//解構賦值(Gulp的不同function)
const { src, dest, watch, series } = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const postcss = require('gulp-postcss');
const cssnano = require('cssnano');
const terser = require('gulp-terser');
//browsersync需要create(),有點像是初始
const browsersync = require('browser-sync').create();
接下來就是建立 Gulp Task。
先來看一下範例結構:
|app/asset/| js/ all.js
| | | style/ all.scss
| |
| |pages/index.html
|gulpfile.js
指令 | 解釋 |
---|---|
gulp.task() | 定義任務,可自訂名稱 |
gulp.src() | 檔案來源 |
gulp.dest() | 處理後的檔案輸出位置 |
gulp.pipe() | 串流來源檔案到另個外掛(串流處理任務的套件) |
gulp.watch () | 監控任務,當檔案有更動時,便會執行相對應的任務 |
//html 複製到dist
function htmlTask(){
return src('app/pages/*.html')
.pipe(dest('dist/pages',{sourcemaps: '.'}))
}
//Sass Task
function scssTask(){
return src('app/asset/style/all.scss',{sourcemaps: true})
.pipe(sass()) //scss function 處理任務
.pipe(postcss([cssnano()])) // postcss function 處理任務
.pipe(dest('dist/style',{sourcemaps: '.'})) // 存在新的dist資料夾
}
//JavaScript Task
function jsTask(){
return src('app/asset/js/all.js')
.pipe(terser()) //壓縮JS檔
.pipe(dest('dist/js',{sourcemaps: '.'}))
}
//Browsersync Tasks(會有兩個)
//first: initialize local server and start running it
function browersyncServe(callback){
browsersync.init({
server: {
baseDir: 'dist/pages' //指向要開啟的資料夾
}
});
callback();
};
//second: reload the server whenever the code is changed
function browsersyncReload(callback){
browsersync.reload();
callback();
};
// callback function: 在 Gulp 中,所有任務都是非同步的 JS function,
//所以如果 function 不需要 return 東西,我們需要使用 callback function 來明確的表示它已經執行完畢。
//其實就是 done 回調函數的用法。
要監看的檔案
當我們做更動時,希望不要一直重新執行,而時只要reload也頁面就好
//watch Task
function watchTask(){
watch(['app/pages/**/*.html',
'app/asset/style/**/*.scss',
'app/asset/js/**/*.js'],
series(htmlTask,scssTask,jsTask,browsersyncReload));
}
把上面我們需要的function加入,Gulp Task
exports.default = series(
htmlTask,
scssTask,
jsTask,
browersyncServe,
watchTask
)
]]>背景環境
npm init
建立package.json
檔案https://gulpjs.com/plugins/ 點入網址就可以看到很多的plugin
在終端機載入
這個練習需要
pluging | purpose |
---|---|
gulp-sass | 編譯sass |
gulp-postcss cssnano | postcss 處理CSS prefix cssnano 優化程式碼 |
gulp-terser | 壓縮 JS |
browser-sync | 瀏覽器同步檢視 |
延伸:
gulp-postcss
: 以往都是使用 Sass 的 Compass 來加入 prefix,這種增加方式就是無差別的加入,但其實現在的 CSS 有 9 成以上的 prefix 都沒有加入的必要,所以就可以使用 postcss
。PostCSS 類似 Sass,但是他是直接編譯 .css
檔案,另外他的套件都是用插件的方式載入(所以像這次的練習,也另外載入了cssnano),並不是像 Sass 已經有固定的寫法。
npm install gulp-scss gulp-postcss cssnano gulp-terser browser-sync --save-dev
當然首先要新增gulpfile.js
接下來就是將我們所有的套件載入
//gulpfile.js
//解構賦值(Gulp的不同function)
const { src, dest, watch, series } = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const postcss = require('gulp-postcss');
const cssnano = require('cssnano');
const terser = require('gulp-terser');
//browsersync需要create(),有點像是初始
const browsersync = require('browser-sync').create();
接下來就是建立 Gulp Task。
先來看一下範例結構:
|app/asset/| js/ all.js
| | | style/ all.scss
| |
| |pages/index.html
|gulpfile.js
指令 | 解釋 |
---|---|
gulp.task() | 定義任務,可自訂名稱 |
gulp.src() | 檔案來源 |
gulp.dest() | 處理後的檔案輸出位置 |
gulp.pipe() | 串流來源檔案到另個外掛(串流處理任務的套件) |
gulp.watch () | 監控任務,當檔案有更動時,便會執行相對應的任務 |
//html 複製到dist
function htmlTask(){
return src('app/pages/*.html')
.pipe(dest('dist/pages',{sourcemaps: '.'}))
}
//Sass Task
function scssTask(){
return src('app/asset/style/all.scss',{sourcemaps: true})
.pipe(sass()) //scss function 處理任務
.pipe(postcss([cssnano()])) // postcss function 處理任務
.pipe(dest('dist/style',{sourcemaps: '.'})) // 存在新的dist資料夾
}
//JavaScript Task
function jsTask(){
return src('app/asset/js/all.js')
.pipe(terser()) //壓縮JS檔
.pipe(dest('dist/js',{sourcemaps: '.'}))
}
//Browsersync Tasks(會有兩個)
//first: initialize local server and start running it
function browersyncServe(callback){
browsersync.init({
server: {
baseDir: 'dist/pages' //指向要開啟的資料夾
}
});
callback();
};
//second: reload the server whenever the code is changed
function browsersyncReload(callback){
browsersync.reload();
callback();
};
// callback function: 在 Gulp 中,所有任務都是非同步的 JS function,
//所以如果 function 不需要 return 東西,我們需要使用 callback function 來明確的表示它已經執行完畢。
//其實就是 done 回調函數的用法。
要監看的檔案
當我們做更動時,希望不要一直重新執行,而時只要reload也頁面就好
//watch Task
function watchTask(){
watch(['app/pages/**/*.html',
'app/asset/style/**/*.scss',
'app/asset/js/**/*.js'],
series(htmlTask,scssTask,jsTask,browsersyncReload));
}
把上面我們需要的function加入,Gulp Task
exports.default = series(
htmlTask,
scssTask,
jsTask,
browersyncServe,
watchTask
)
]]>
要做到這件事,只要在專案目錄裡放一個 .gitignore 檔案,並且設定想要忽略的規則就行了。
.gitignore
檔案touch .gitignore
假設不想讓node_module
檔案進入git,直接想上名字就可。
node_module
config/datafile
/db/*.sqlite3
*.tmp
.gitignore
只要 .gitignore 這個檔案存在,即使這個沒被 Commit 或是沒被 Push 上 Git Server 一樣會有效果。但這個檔案會建議 Commit 進專案並且推上 Git Server,這樣一來整個專案一起開發的人可以共享相同的設定。
不同程式檔案常見的各種忽略
https://github.com/github/gitignore
若檔案在.gitignore
存在前已經存在,不會被忽略到。
.gitignore
檔案設定的規則,只對在規則設定之後的有效,那些已經存在的檔案就像既得利益者一樣,這些規則是對他們沒有效果的。
如果想套用 .gitignore
的規則,就必須先使用 git rm --cached
指令把這些既得利益者請出 Git,移出 Git 控管之後,它就會開始會被忽略了。
如果想要一次清除那些已經被忽略的檔案,可以使用 git clean
指令並配合 -X
參數:
$ git clean -fX
那個額外加上的 -f 參數是指強制刪除的意思,這樣一來就可清除那些被忽略的檔案。
要做到這件事,只要在專案目錄裡放一個 .gitignore 檔案,並且設定想要忽略的規則就行了。
.gitignore
檔案touch .gitignore
假設不想讓node_module
檔案進入git,直接想上名字就可。
node_module
config/datafile
/db/*.sqlite3
*.tmp
.gitignore
只要 .gitignore 這個檔案存在,即使這個沒被 Commit 或是沒被 Push 上 Git Server 一樣會有效果。但這個檔案會建議 Commit 進專案並且推上 Git Server,這樣一來整個專案一起開發的人可以共享相同的設定。
不同程式檔案常見的各種忽略
https://github.com/github/gitignore
若檔案在.gitignore
存在前已經存在,不會被忽略到。
.gitignore
檔案設定的規則,只對在規則設定之後的有效,那些已經存在的檔案就像既得利益者一樣,這些規則是對他們沒有效果的。
如果想套用 .gitignore
的規則,就必須先使用 git rm --cached
指令把這些既得利益者請出 Git,移出 Git 控管之後,它就會開始會被忽略了。
如果想要一次清除那些已經被忽略的檔案,可以使用 git clean
指令並配合 -X
參數:
$ git clean -fX
那個額外加上的 -f 參數是指強制刪除的意思,這樣一來就可清除那些被忽略的檔案。
var
、let
、const
這三者在JavaScript皆是用來宣告變數,最大的差別是他們的作用域(scope)。
var
: 作用範圍是function
。
let
、const
: 作用範圍是block
/{}
。
let
、const
的差別const
所宣告的變數還無法被重新賦值 (re-assign)。
這兩篇是關於 Vue的元件。
app.component 和 export default ?
多個 Component 元件如何組在一起?
我們可以把每個JavaScript檔案當作是一個獨立的模組,在使用import
(匯入)、export
(匯出)。
下面這篇文章有簡單說明 this 的指向
this 與 call() / apply() / bind(),apply(),bind()/)
bind()
指定。在ES6出現的箭頭函式/)
過去我們組合JS變數與HTML模板時,大多是透過 +
字串結合的方式,或透過陣列來新增字串,最後再用[].join("")
的方式接起來。
到了ES6,就可以簡單的用字串模板啦!
const name = 'Bibo'
const newString = ` Hi, I am ${name}`
ES6的解構賦值語法可以把陣列或物件內的資料解開變成獨立變數。
const person = {
name: 'Ted',
age: 38,
family:{
dad: 'Bob',
mom: 'June'
}
}
const {name,age,family} = person;
consol.log(name); //'Ted'
const num = [1,2,3,4,5];
const [x,y] = num;
console.log(x) //1
展開運算子 通常用在 陣列 或是 函式的參數。
const data = ['010','020','030'];
const arr = ['000',...data];
console.log(arr) // ['000',010','020','030']
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// expected output: 6
將陣列內的東西分離
console.log(arr) // ['000',010','020','030']
const [a,b,...others] = arr //解構賦值
console.log(a) //'000'
console.log(b) //'010'
console.log(others) //'020','030'
為了解決同步與非同步的問題,ES6出現了 promise
物件。
關於 JavaScript Promises
關於 JavaScript Async Await
var
、let
、const
這三者在JavaScript皆是用來宣告變數,最大的差別是他們的作用域(scope)。
var
: 作用範圍是function
。
let
、const
: 作用範圍是block
/{}
。
let
、const
的差別const
所宣告的變數還無法被重新賦值 (re-assign)。
這兩篇是關於 Vue的元件。
app.component 和 export default ?
多個 Component 元件如何組在一起?
我們可以把每個JavaScript檔案當作是一個獨立的模組,在使用import
(匯入)、export
(匯出)。
下面這篇文章有簡單說明 this 的指向
this 與 call() / apply() / bind(),apply(),bind()/)
bind()
指定。在ES6出現的箭頭函式/)
過去我們組合JS變數與HTML模板時,大多是透過 +
字串結合的方式,或透過陣列來新增字串,最後再用[].join("")
的方式接起來。
到了ES6,就可以簡單的用字串模板啦!
const name = 'Bibo'
const newString = ` Hi, I am ${name}`
ES6的解構賦值語法可以把陣列或物件內的資料解開變成獨立變數。
const person = {
name: 'Ted',
age: 38,
family:{
dad: 'Bob',
mom: 'June'
}
}
const {name,age,family} = person;
consol.log(name); //'Ted'
const num = [1,2,3,4,5];
const [x,y] = num;
console.log(x) //1
展開運算子 通常用在 陣列 或是 函式的參數。
const data = ['010','020','030'];
const arr = ['000',...data];
console.log(arr) // ['000',010','020','030']
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// expected output: 6
將陣列內的東西分離
console.log(arr) // ['000',010','020','030']
const [a,b,...others] = arr //解構賦值
console.log(a) //'000'
console.log(b) //'010'
console.log(others) //'020','030'
為了解決同步與非同步的問題,ES6出現了 promise
物件。
關於 JavaScript Promises
關於 JavaScript Async Await
相關文章 關於 mount 生命週期
主要差別就是使用setup代替了之前的beforeCreate和created
Vue2.x | Vue3 |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured |
相關文章 什麼是 Proxy ? 與 Defined Property 的不同?
vue2.x雙向繫結的核心是Object.defineProperty(),到了 Vue3 變成使用 proxy的語法。
在Vue2裡面,當我們把一個JavaScript對象傳給Vue的data
選項,Vue會使用Object.defineProperty()
將與此JavaScript對象有關的所有屬性轉為getter/setter
。
getter/setter
對我們來講,在使用Vue時你不會看到它,但是在內部他們讓Vue追蹤依賴,在屬性被訪問或修改時通知 watcher
,watcher
會尋找與這個屬性有哪些連動的元件,並一個一個去觸發它,並且要求這些元件要重新繪製。
透過這樣的邏輯就可以讓 Vue 將資料與畫面切分開來。
連結文章: Vue3 Proxy ? Vue2 Define Property 的不同?
其實還有很多的差別,但今天就先到這啦
]]>相關文章 關於 mount 生命週期
主要差別就是使用setup代替了之前的beforeCreate和created
Vue2.x | Vue3 |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured |
相關文章 什麼是 Proxy ? 與 Defined Property 的不同?
vue2.x雙向繫結的核心是Object.defineProperty(),到了 Vue3 變成使用 proxy的語法。
在Vue2裡面,當我們把一個JavaScript對象傳給Vue的data
選項,Vue會使用Object.defineProperty()
將與此JavaScript對象有關的所有屬性轉為getter/setter
。
getter/setter
對我們來講,在使用Vue時你不會看到它,但是在內部他們讓Vue追蹤依賴,在屬性被訪問或修改時通知 watcher
,watcher
會尋找與這個屬性有哪些連動的元件,並一個一個去觸發它,並且要求這些元件要重新繪製。
透過這樣的邏輯就可以讓 Vue 將資料與畫面切分開來。
連結文章: Vue3 Proxy ? Vue2 Define Property 的不同?
其實還有很多的差別,但今天就先到這啦
]]>var money = { total : 100 };
money.total = 300;
console.log(money.total); //300
上面這樣的做法,會直接將money.total = 300;
的值,賦蓋到原本的屬性上。
若我們希望在取值或覆蓋時,可以做一些運算或調整,就可以用到Getter 與 Setter。
Getter: 取得特定值的方法
Setter: 存值的方法
使用 get、set關鍵字去設定getter、setter 方法。
var money = {
total : 100
set save(price){
//這裡就是一個函式的概念,可以傳入參數
this.total += price
},
get saving(){
//get是取值的概念,不會傳入參數
return this.total
}
};
money.save = 300; //300會透過參數的方式傳入save函式
console.log(money.saving); //400
console.log(money.total); //400
money.save = 300; //300會透過參數的方式傳入save函式
console.log(money.saving); //700
console.log(money.total); //700
使用 Object.defineprooerty()
設定屬性特徵的語法,去設定getter、setter
Object.defineProperty(money,'save',{
set: function(price){this.total += price },
get: function(){return this.total}
})
//'save'的屬性特徵的預設是不可被刪除、也不可被列舉的
使用 Object.defineprooerty()
在陣列原型內加入一個屬性特徵。
Object.defineProperty{ Array.prototype, 'lastest', {
get: function(){
return this[this.length-1];
//取得最後一個資料
}
}
}
//因為是在陣列原型加入,所有的陣列都可以用
var a = [1,2,3]
console.log(a.lastest); // 3
]]>var money = { total : 100 };
money.total = 300;
console.log(money.total); //300
上面這樣的做法,會直接將money.total = 300;
的值,賦蓋到原本的屬性上。
若我們希望在取值或覆蓋時,可以做一些運算或調整,就可以用到Getter 與 Setter。
Getter: 取得特定值的方法
Setter: 存值的方法
使用 get、set關鍵字去設定getter、setter 方法。
var money = {
total : 100
set save(price){
//這裡就是一個函式的概念,可以傳入參數
this.total += price
},
get saving(){
//get是取值的概念,不會傳入參數
return this.total
}
};
money.save = 300; //300會透過參數的方式傳入save函式
console.log(money.saving); //400
console.log(money.total); //400
money.save = 300; //300會透過參數的方式傳入save函式
console.log(money.saving); //700
console.log(money.total); //700
使用 Object.defineprooerty()
設定屬性特徵的語法,去設定getter、setter
Object.defineProperty(money,'save',{
set: function(price){this.total += price },
get: function(){return this.total}
})
//'save'的屬性特徵的預設是不可被刪除、也不可被列舉的
使用 Object.defineprooerty()
在陣列原型內加入一個屬性特徵。
Object.defineProperty{ Array.prototype, 'lastest', {
get: function(){
return this[this.length-1];
//取得最後一個資料
}
}
}
//因為是在陣列原型加入,所有的陣列都可以用
var a = [1,2,3]
console.log(a.lastest); // 3
]]>
var person = {
a: 2,
b: 4,
}
a
為屬型, 2
這個值就是特徵之一(可以修改、寫入)。
屬性的特徵可以調整或是定義。
Object.definedProperty( Object, '屬性' , {參數} )
透過這個語法,我們可以針對物件屬性調整這些項特徵:
//屬性以字串寫入
Object.definedProperty( person, 'a' , {
value: 8, //預設情況下是2,由於writable是true,所以可以寫入
writable: true,
configurable: true,
enumerable: true
} )
console.log(person) //{ a: 8,b: 4}
指的是這個屬性的值可不可以被調整
//屬性以字串寫入
Object.definedProperty( person, 'a' , {
value: 8, //預設情況下是2,由於writable是true,所以可以寫入
writable: false, // 為false時不可被更改
configurable: true,
enumerable: true
} )
person.a = 4; // writable為false時不可被更改
console.log(person) //{ a: 8,b: 4}
Console.log出來並不會因為 它的寫入特徵為false還去更改它的值 ,這件錯誤跳錯。
因為這個錯誤屬於噤默錯誤。
但是若在use strict
嚴謹模式下會報錯。
指的是這個屬性的值可不可以被刪除
Object.definedProperty( person, 'a' , {
configurable: false, //為false時不可被刪除
} )
delete person.a; // configurable為false時不可被刪除
delete person.b;
console.log(person) //{a: 8}
指的是這個屬性的值可不可以被列舉(讀取)
若是想要一一把屬性讀出,可以使用for(變數 in 物件)
的方法。
Object.definedProperty( person, 'a' , {
enumerable: false, //為false時不可被列舉
} )
for(var i in person){
console.log(i)
}
//{b: 4}
Object.definedProperty( person, 'd' , {
value: {},
writable: false, // 為false時不可被更改
} )
person.d.a = 4;
// 它是針對內層更改,這樣是可以的。因為Object.defineProperty只能做到淺層保護
console.log(person)
//{ a: 8,
// b: 4,
// d: { a: 4 }
// }
Object.definedProperty( person, {
a:{value:3,...},
b:{writable: false}
}
} )
var person = {
a: 2,
b: 4,
}
a
為屬型, 2
這個值就是特徵之一(可以修改、寫入)。
屬性的特徵可以調整或是定義。
Object.definedProperty( Object, '屬性' , {參數} )
透過這個語法,我們可以針對物件屬性調整這些項特徵:
//屬性以字串寫入
Object.definedProperty( person, 'a' , {
value: 8, //預設情況下是2,由於writable是true,所以可以寫入
writable: true,
configurable: true,
enumerable: true
} )
console.log(person) //{ a: 8,b: 4}
指的是這個屬性的值可不可以被調整
//屬性以字串寫入
Object.definedProperty( person, 'a' , {
value: 8, //預設情況下是2,由於writable是true,所以可以寫入
writable: false, // 為false時不可被更改
configurable: true,
enumerable: true
} )
person.a = 4; // writable為false時不可被更改
console.log(person) //{ a: 8,b: 4}
Console.log出來並不會因為 它的寫入特徵為false還去更改它的值 ,這件錯誤跳錯。
因為這個錯誤屬於噤默錯誤。
但是若在use strict
嚴謹模式下會報錯。
指的是這個屬性的值可不可以被刪除
Object.definedProperty( person, 'a' , {
configurable: false, //為false時不可被刪除
} )
delete person.a; // configurable為false時不可被刪除
delete person.b;
console.log(person) //{a: 8}
指的是這個屬性的值可不可以被列舉(讀取)
若是想要一一把屬性讀出,可以使用for(變數 in 物件)
的方法。
Object.definedProperty( person, 'a' , {
enumerable: false, //為false時不可被列舉
} )
for(var i in person){
console.log(i)
}
//{b: 4}
Object.definedProperty( person, 'd' , {
value: {},
writable: false, // 為false時不可被更改
} )
person.d.a = 4;
// 它是針對內層更改,這樣是可以的。因為Object.defineProperty只能做到淺層保護
console.log(person)
//{ a: 8,
// b: 4,
// d: { a: 4 }
// }
Object.definedProperty( person, {
a:{value:3,...},
b:{writable: false}
}
} )
之前有談過 Object.defindProperty()
,它可以針對 屬性特徵 來做調整。
這篇要來介紹另外三個物件語法,它們可以針對 物件本身 做調整。
var person = {
a:1,
b:2,
c:{}
}
語法:
Object.preventExtension(Object)
針對物件本身(不是物件屬性),防止物件擴充 Object.isExtensible(Object)
查看物件是否可被擴充Object.getOwnPropertyDescriptior(Object,'屬性')
取得物件內的屬性特徵Object.preventExtension(person);
console.log(Object.isExtensible(person)); // false 針對物件本身不可被擴充
console.log(Object.getOwnPropertyDescriptior(person,'a'))
//{value:1, writable:true,configutable: true,enumarable: true}
//可以看到它的寫入還是true,是因為防止擴充是針對物件本身,不會影響物件的屬性特徵
person.a = 'a'; // 物件的屬性特徵還是可以寫入
person.d = 'd'; // 不能針對物件寫入!!!
person.c.a = '可以寫入' //可以巢狀新增
語法: Object.seal(Object)
透過Seal封裝的物件將無法新增、刪除和重新配置特徵。但是可以寫入!!
Seal
加入preventExtensions
的概念,針對物件無法寫入。
Object.seal(person);
console.log(Object.isExtensible(person)); // False 使用seal會同時防止物件擴充
console.log(Object.issealable(person)); // True
console.log(Object.getOwnPropertyDescriptior(person,'a'))
//{value:1, writable:true,configutable: false,enumarable: true}
//無法刪除
person.a = 'a'; // 物件的屬性特徵還是可以寫入
person.d = 'd'; // 不能針對物件寫入!!!
person.c.a = '可以寫入' //可以巢狀新增
Freeze
加上seal
的概念。
除了針對物件無法擴充、刪除,它也無法調整值。
Object.freeze(person)
var person = {
a:1, //只凍結此處
b:2,//只凍結此處
c:{}//只凍結此處
}
//可以整個重新賦值
person = {};
]]>之前有談過 Object.defindProperty()
,它可以針對 屬性特徵 來做調整。
這篇要來介紹另外三個物件語法,它們可以針對 物件本身 做調整。
var person = {
a:1,
b:2,
c:{}
}
語法:
Object.preventExtension(Object)
針對物件本身(不是物件屬性),防止物件擴充 Object.isExtensible(Object)
查看物件是否可被擴充Object.getOwnPropertyDescriptior(Object,'屬性')
取得物件內的屬性特徵Object.preventExtension(person);
console.log(Object.isExtensible(person)); // false 針對物件本身不可被擴充
console.log(Object.getOwnPropertyDescriptior(person,'a'))
//{value:1, writable:true,configutable: true,enumarable: true}
//可以看到它的寫入還是true,是因為防止擴充是針對物件本身,不會影響物件的屬性特徵
person.a = 'a'; // 物件的屬性特徵還是可以寫入
person.d = 'd'; // 不能針對物件寫入!!!
person.c.a = '可以寫入' //可以巢狀新增
語法: Object.seal(Object)
透過Seal封裝的物件將無法新增、刪除和重新配置特徵。但是可以寫入!!
Seal
加入preventExtensions
的概念,針對物件無法寫入。
Object.seal(person);
console.log(Object.isExtensible(person)); // False 使用seal會同時防止物件擴充
console.log(Object.issealable(person)); // True
console.log(Object.getOwnPropertyDescriptior(person,'a'))
//{value:1, writable:true,configutable: false,enumarable: true}
//無法刪除
person.a = 'a'; // 物件的屬性特徵還是可以寫入
person.d = 'd'; // 不能針對物件寫入!!!
person.c.a = '可以寫入' //可以巢狀新增
Freeze
加上seal
的概念。
除了針對物件無法擴充、刪除,它也無法調整值。
Object.freeze(person)
var person = {
a:1, //只凍結此處
b:2,//只凍結此處
c:{}//只凍結此處
}
//可以整個重新賦值
person = {};
]]>
JavaScript 有一個特性是源自於 Java,就是可以使用 new
語法來新增一個物件。不過兩者的根本運作是不同的。
使用new
語法新增的物件,會有繼承的特性。
在Java內屬於類別繼承,而在JavaScript內屬於原型繼承。
JavaScript 就只有一個建構子:物件。
JavaScript並沒有類別的概念,所以需要用原型的方式,做出如同類別繼承的方法。
每當我們建立一個物件實體,這個物件實體會有一個連著其他原型(prototype)的私有屬性(private property)物件。這個原型物件也有著自己的原型,於是原型物件就這樣鏈結,直到撞見 null 為止:null 在定義裡沒有原型、也是原型鏈(prototype chain)的最後一個鏈結。
var a = [1,2,3]
console.log(a)//[1,2,3]
這個 proto 就是上面a陣列的原型,所有的物件實體都可以利用.
來取用屬性與方法。
a.forEach((i)=>console.log(i))
我們也可以透過語法在原型上新增方法
Array.prototype.getLast = function(){...}
主要重點:
//建構函式 -- 藍圖
function Member(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
//透過 prototype 新增方法
Member.prototype.hello = function(){
console.log('Hi' + this.name)
}
目前它還只是一個建構函式,我們可以把它想成藍圖。若要讓它成為一個實體需要透過new
。this
綁定在建構物件上。Andy.hello() // 'Hi Andy'
``
目前Andy這個物件就有自己的屬性,點進去
proto`會看到constructor是從Member這個物件出來的。
自訂原型的屬性特徵可以被列舉。原生原型的屬性特徵不能被列舉。
]]>JavaScript 有一個特性是源自於 Java,就是可以使用 new
語法來新增一個物件。不過兩者的根本運作是不同的。
使用new
語法新增的物件,會有繼承的特性。
在Java內屬於類別繼承,而在JavaScript內屬於原型繼承。
JavaScript 就只有一個建構子:物件。
JavaScript並沒有類別的概念,所以需要用原型的方式,做出如同類別繼承的方法。
每當我們建立一個物件實體,這個物件實體會有一個連著其他原型(prototype)的私有屬性(private property)物件。這個原型物件也有著自己的原型,於是原型物件就這樣鏈結,直到撞見 null 為止:null 在定義裡沒有原型、也是原型鏈(prototype chain)的最後一個鏈結。
var a = [1,2,3]
console.log(a)//[1,2,3]
這個 proto 就是上面a陣列的原型,所有的物件實體都可以利用.
來取用屬性與方法。
a.forEach((i)=>console.log(i))
我們也可以透過語法在原型上新增方法
Array.prototype.getLast = function(){...}
主要重點:
//建構函式 -- 藍圖
function Member(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
//透過 prototype 新增方法
Member.prototype.hello = function(){
console.log('Hi' + this.name)
}
目前它還只是一個建構函式,我們可以把它想成藍圖。若要讓它成為一個實體需要透過new
。this
綁定在建構物件上。Andy.hello() // 'Hi Andy'
``
目前Andy這個物件就有自己的屬性,點進去
proto`會看到constructor是從Member這個物件出來的。
自訂原型的屬性特徵可以被列舉。原生原型的屬性特徵不能被列舉。
]]>this
的指向有五個重點:
call()
/ apply()
/ bind()
指定 this
。this
,指向是全域。this
,指向是全域。fn.call(this,para1,para2...)
使用call執行函式,第一個參數是指this
的地方,後面才是函式的參數。
call會立刻執行,回傳function執行結果
fn.call(this,[para1,para2...])
apply 和 call 類似,只是後面的參數要用陣列表示,apply 只允許兩個變數傳入。
回傳function執行結果
var fn2 = fn.bind(this,,para1,para2...);
fn2();
fn.bind(this,,para1,para2...);
回傳的是綁定 this 後的原函數。
所以bind不會立刻執行,需要再去調用它。
範例:
var obj = {
x: 81,
getX: function() {
return this.x;
}
};
alert(obj.getX.bind(obj)());
alert(obj.getX.call(obj));
alert(obj.getX.apply(obj));
我自己的想法是,bind和其他倆者不同的是,它可以先綁定好this,之後再執行。
Use .bind() when you want that function to later be called with a certain context, useful in events.
Use .call() or .apply() when you want to invoke the function immediately, and modify the context.
this
的指向有五個重點:
call()
/ apply()
/ bind()
指定 this
。this
,指向是全域。this
,指向是全域。fn.call(this,para1,para2...)
使用call執行函式,第一個參數是指this
的地方,後面才是函式的參數。
call會立刻執行,回傳function執行結果
fn.call(this,[para1,para2...])
apply 和 call 類似,只是後面的參數要用陣列表示,apply 只允許兩個變數傳入。
回傳function執行結果
var fn2 = fn.bind(this,,para1,para2...);
fn2();
fn.bind(this,,para1,para2...);
回傳的是綁定 this 後的原函數。
所以bind不會立刻執行,需要再去調用它。
範例:
var obj = {
x: 81,
getX: function() {
return this.x;
}
};
alert(obj.getX.bind(obj)());
alert(obj.getX.call(obj));
alert(obj.getX.apply(obj));
我自己的想法是,bind和其他倆者不同的是,它可以先綁定好this,之後再執行。
Use .bind() when you want that function to later be called with a certain context, useful in events.
Use .call() or .apply() when you want to invoke the function immediately, and modify the context.
const name = "Ted";
function printName(){
console.log(name)
}
printName() // Ted
上面是很平常就可以看到的程式碼,其實這一整個程式碼就是一個Closure 閉包。
Closure 閉包。就是指我們可以取得函式外部的變數值的範圍(作用域)。
每個Scope都可以取得外層Scope的東西
const name = "Ted";
function printName(){
console.log(name)
}
printName() // Ted
function printName(){
console.log(name)
}
大部分的範例都會以包在函式內的函式來說明:
function outerFunction(outerVariable){
return function innerFunction(innerVariable){
console.log('Outer Variable:' + outerVariable)
console.log('Inner Variable:' + innerVariable)
}
}
const newFunction = outerFunction('outside')
newFunction('inside')
// Outer Variable: outside
// Inner Variable: inside
通常function內的變數,在沒被呼叫時,記憶體就會將它釋放。而閉包因為知道還會用到,所以就不會釋放。
]]>const name = "Ted";
function printName(){
console.log(name)
}
printName() // Ted
上面是很平常就可以看到的程式碼,其實這一整個程式碼就是一個Closure 閉包。
Closure 閉包。就是指我們可以取得函式外部的變數值的範圍(作用域)。
每個Scope都可以取得外層Scope的東西
const name = "Ted";
function printName(){
console.log(name)
}
printName() // Ted
function printName(){
console.log(name)
}
大部分的範例都會以包在函式內的函式來說明:
function outerFunction(outerVariable){
return function innerFunction(innerVariable){
console.log('Outer Variable:' + outerVariable)
console.log('Inner Variable:' + innerVariable)
}
}
const newFunction = outerFunction('outside')
newFunction('inside')
// Outer Variable: outside
// Inner Variable: inside
通常function內的變數,在沒被呼叫時,記憶體就會將它釋放。而閉包因為知道還會用到,所以就不會釋放。
]]>範例物件:
var family={
name: 'Bob',
members:{
mom: 'Betty',
dad: 'Sean'
}
}
方法: for in
、JQuery.extend
、ES6寫法
只能複製最外層,內層還是會以傳參考的方式(所以會互相影響)
for in
先定義一個新物件:
var newFamily={};
for (var key in family){
newFamily[key] = family[key]
}
試著修改外層:
newFamily.name='Andy';
console.log(family, newFamily)
試著修改內層:
newFamily.member.mom='Keira';
console.log(family, newFamily)
內層還是會一起更改,這就是淺層複製。
JQuery.extend
JQuery.extend 是用來將多個物件合起來,並放到第一個物件:
$.extend(dest,src1,src2,src3...)
將src合併,放到dest
var newResult = $.extend({},{name:'Tom',age: 20 },{name: 'Ted',sex: 'male'} )
console.log(newResult)//{name: 'Ted',age: 20, sex: 'male'}
以 後面 蓋 前面 的方式合併
Object.assign()
(ES6寫法)用來複製一個或多個物件自身所有可數的屬性到另一個目標物件。回傳的值為該目標物件。
語法: Object.assign(target, ...sources)
var newFamily = object.assign({},family)
方法:將物件轉為字串,再轉回物件
var newFamily = JSON.parsse(JSON.stringify(family));
可以測試看看,會發現內層也可以以傳值的方式被路複製了! 主要是因為它一開始就先轉為字串,以傳值的方式複製。
]]>範例物件:
var family={
name: 'Bob',
members:{
mom: 'Betty',
dad: 'Sean'
}
}
方法: for in
、JQuery.extend
、ES6寫法
只能複製最外層,內層還是會以傳參考的方式(所以會互相影響)
for in
先定義一個新物件:
var newFamily={};
for (var key in family){
newFamily[key] = family[key]
}
試著修改外層:
newFamily.name='Andy';
console.log(family, newFamily)
試著修改內層:
newFamily.member.mom='Keira';
console.log(family, newFamily)
內層還是會一起更改,這就是淺層複製。
JQuery.extend
JQuery.extend 是用來將多個物件合起來,並放到第一個物件:
$.extend(dest,src1,src2,src3...)
將src合併,放到dest
var newResult = $.extend({},{name:'Tom',age: 20 },{name: 'Ted',sex: 'male'} )
console.log(newResult)//{name: 'Ted',age: 20, sex: 'male'}
以 後面 蓋 前面 的方式合併
Object.assign()
(ES6寫法)用來複製一個或多個物件自身所有可數的屬性到另一個目標物件。回傳的值為該目標物件。
語法: Object.assign(target, ...sources)
var newFamily = object.assign({},family)
方法:將物件轉為字串,再轉回物件
var newFamily = JSON.parsse(JSON.stringify(family));
可以測試看看,會發現內層也可以以傳值的方式被路複製了! 主要是因為它一開始就先轉為字串,以傳值的方式複製。
]]>JavaScript是一個動態型別的語言,它的型別會在執行過程中不斷地轉換,代表當在執行階段才會被賦予型別。這個被動態賦予的型別又分為兩大類
原始型別與物件型別。
當變數的值是 原生型別(primitive),行為是傳值。
當變數的值是 物件(object),行為是傳參考。
用拷貝的方式,所以修改不會互相影響。
var x = 5;
var y = x;
console.log(x,y) // 5,5
x=10;
console.log(x,y) // 10 ,5
-和傳值不同的是,傳參照不是拷貝是 "傳參數位置",修改會互相影響
var person 1 = {num:111};
var person2 = person 1;
console.log(person 1, person 2) // {num:111},{num:111}
person 1.num = 222;
console.log(person 1, person 2) // {num:222},{num:222}
像是前兩種的融合。雖然是物件型別,但對其一物件變數做重新賦值時,不會互相影響。
var person 3 = {num:333};
perons 1 = person 3;
console.log(person 1, person 2,person 3)//{num:333},{num:111},{num:333}
這裡跟 Call by Reference 不同的地方是 **重新賦值**。
-----
例題:
function changeAge(person) {
person.age = 25;
return person
}
var personObj1 = {
name: 'Charles',
age: 30
};
var personObj2 = changeAge(personObj1);
console.log(personObj1); // { name: 'Charles', age: 25 }
console.log(personObj2); // { name: 'Charles', age: 25 }
function changeAge(person) {
person.age = 25;
person = {
name: 'John',
age: 50
}
return person
}
var personObj1 = {
name: 'Charles',
age: 30
};
var personObj2 = changeAge(personObj1);
console.log(personObj1);
console.log(personObj2);
```
JavaScript是一個動態型別的語言,它的型別會在執行過程中不斷地轉換,代表當在執行階段才會被賦予型別。這個被動態賦予的型別又分為兩大類
原始型別與物件型別。
當變數的值是 原生型別(primitive),行為是傳值。
當變數的值是 物件(object),行為是傳參考。
用拷貝的方式,所以修改不會互相影響。
var x = 5;
var y = x;
console.log(x,y) // 5,5
x=10;
console.log(x,y) // 10 ,5
-和傳值不同的是,傳參照不是拷貝是 "傳參數位置",修改會互相影響
var person 1 = {num:111};
var person2 = person 1;
console.log(person 1, person 2) // {num:111},{num:111}
person 1.num = 222;
console.log(person 1, person 2) // {num:222},{num:222}
像是前兩種的融合。雖然是物件型別,但對其一物件變數做重新賦值時,不會互相影響。
var person 3 = {num:333};
perons 1 = person 3;
console.log(person 1, person 2,person 3)//{num:333},{num:111},{num:333}
這裡跟 Call by Reference 不同的地方是 **重新賦值**。
-----
例題:
function changeAge(person) {
person.age = 25;
return person
}
var personObj1 = {
name: 'Charles',
age: 30
};
var personObj2 = changeAge(personObj1);
console.log(personObj1); // { name: 'Charles', age: 25 }
console.log(personObj2); // { name: 'Charles', age: 25 }
function changeAge(person) {
person.age = 25;
person = {
name: 'John',
age: 50
}
return person
}
var personObj1 = {
name: 'Charles',
age: 30
};
var personObj2 = changeAge(personObj1);
console.log(personObj1);
console.log(personObj2);
```
defineProperty
的一個特性就是它只能對物件中其中一個屬性進行劫持。
這樣會碰到一個問題,當新增新的物件屬性obj.a = 1
會無法被vue2劫持,必須使用vue2提供的$set方法來進行更新。
const a = { b: 1 };
Object.defineProperty(a,'b',{
set: function() {},
get: function() {}
});
當我們給a物件新增一個屬性的時候,當前新增的屬性並沒有被defineProperty
劫持,雖然在對應的物件上依舊成功的生成了一個新的屬性,但是由於vue2是通過defineProperty的setter與getter進行資料劫持的,既然新增的資料並沒有被劫持,所以無論怎麼更新,頁面依舊不會重新渲染。
Proxy 可以理解成,在目標對象之前架設一層攔截,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。
proxy也可看做對於資料的代理,它能夠響應新增的屬性,當新增一個屬性的時候,可以響應到get中,對當前物件進行代理
參考文章:
https://es6.ruanyifeng.com/#docs/proxy
https://www.796t.com/article.php?id=133363
https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/697272/
defineProperty
的一個特性就是它只能對物件中其中一個屬性進行劫持。
這樣會碰到一個問題,當新增新的物件屬性obj.a = 1
會無法被vue2劫持,必須使用vue2提供的$set方法來進行更新。
const a = { b: 1 };
Object.defineProperty(a,'b',{
set: function() {},
get: function() {}
});
當我們給a物件新增一個屬性的時候,當前新增的屬性並沒有被defineProperty
劫持,雖然在對應的物件上依舊成功的生成了一個新的屬性,但是由於vue2是通過defineProperty的setter與getter進行資料劫持的,既然新增的資料並沒有被劫持,所以無論怎麼更新,頁面依舊不會重新渲染。
Proxy 可以理解成,在目標對象之前架設一層攔截,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。
proxy也可看做對於資料的代理,它能夠響應新增的屬性,當新增一個屬性的時候,可以響應到get中,對當前物件進行代理
參考文章:
https://es6.ruanyifeng.com/#docs/proxy
https://www.796t.com/article.php?id=133363
https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/697272/
MVVM 是 Model-View-ViewModel 的簡寫。即 模型 -視圖 -視圖模型。
模型 是後端傳遞的數據。
視圖 是所看到的頁面。
視圖模型 是 MVVM 的核⼼,它是連接view和model的橋樑。
什麼是雙向綁定呢?
方向一: 模型 --> 試圖。透過數據綁定,將後端傳遞的數據轉化成所看到的頁面。
方向二: 試圖 --> 模型。透過DOM監聽事件,將所看到的頁面轉化成後端數據。
兩個方向都可以實現就是雙向綁定。
實現的方式是透過ViewModel。
ViewModel 像是 view 的代理程式,它負責值皆對 Model 做溝通,而 view 可以透過一些機制來和 viewModel 溝通已取得資料或將資料拋給 model。
MVC是 Model-View-Controller的簡寫。即模型-視圖-控制器。
圖片來源: Youtube channel: WebDevSimplified
M、V、C 三者各司其職,確保不管事更動哪一方時,其他方不會受到影響。
Model 和 View 基本上是有相關的,但是不會有直接的相依關係,他們是由Controller去決定Model產生的資料,再丟到View去呈現。
也就是說,Controller 是 Model 和 View 之間的協調者。Model 和 View不能直接溝通,以確保責任分離。
當接觸的專案逐漸擴大時,往往MVC架構便會使得ViewController程式碼日漸增長而演變成(Massive View Controller),儘管多用extension或是各種命名的Manager去拆分ViewController的工作,但始終脫離不了肥大的命運。
而MVVM可以讓我們容易維護和使用方便。
漸進式者的就是: 想要用哪部分都可以自行決定。有點像是你買一個可拆式工具箱,可以依照自己的需求選擇工具。假使我們專案不需要用到Vue-router,便可以不需要全部載入,可以僅使用core即可
1.關注點分離,意思就是Vue只關注畫面與資料的變化,我們不用手動更新操作DOM元素。(雙向綁定)
2.操作物件模型為主的開發模式(就是操作資料)
3.模板語法
4.元件化
當數據變更時,VUE會幫你更新所有網頁中⽤到它的地⽅。
通過Object.defineProperty()
來劫持各個属性的setter
、getter
,在數據變動時發布消息出去,再觸發相應的監聽。
MVVM 是 Model-View-ViewModel 的簡寫。即 模型 -視圖 -視圖模型。
模型 是後端傳遞的數據。
視圖 是所看到的頁面。
視圖模型 是 MVVM 的核⼼,它是連接view和model的橋樑。
什麼是雙向綁定呢?
方向一: 模型 --> 試圖。透過數據綁定,將後端傳遞的數據轉化成所看到的頁面。
方向二: 試圖 --> 模型。透過DOM監聽事件,將所看到的頁面轉化成後端數據。
兩個方向都可以實現就是雙向綁定。
實現的方式是透過ViewModel。
ViewModel 像是 view 的代理程式,它負責值皆對 Model 做溝通,而 view 可以透過一些機制來和 viewModel 溝通已取得資料或將資料拋給 model。
MVC是 Model-View-Controller的簡寫。即模型-視圖-控制器。
圖片來源: Youtube channel: WebDevSimplified
M、V、C 三者各司其職,確保不管事更動哪一方時,其他方不會受到影響。
Model 和 View 基本上是有相關的,但是不會有直接的相依關係,他們是由Controller去決定Model產生的資料,再丟到View去呈現。
也就是說,Controller 是 Model 和 View 之間的協調者。Model 和 View不能直接溝通,以確保責任分離。
當接觸的專案逐漸擴大時,往往MVC架構便會使得ViewController程式碼日漸增長而演變成(Massive View Controller),儘管多用extension或是各種命名的Manager去拆分ViewController的工作,但始終脫離不了肥大的命運。
而MVVM可以讓我們容易維護和使用方便。
漸進式者的就是: 想要用哪部分都可以自行決定。有點像是你買一個可拆式工具箱,可以依照自己的需求選擇工具。假使我們專案不需要用到Vue-router,便可以不需要全部載入,可以僅使用core即可
1.關注點分離,意思就是Vue只關注畫面與資料的變化,我們不用手動更新操作DOM元素。(雙向綁定)
2.操作物件模型為主的開發模式(就是操作資料)
3.模板語法
4.元件化
當數據變更時,VUE會幫你更新所有網頁中⽤到它的地⽅。
通過Object.defineProperty()
來劫持各個属性的setter
、getter
,在數據變動時發布消息出去,再觸發相應的監聽。
gulp 是 task runner 任務管理工具。
Webpack 是 module bundler 模組整合工具。
Webpack 和 gulp 兩個是不重疊的,他們本身都有 95% 的功能是不能被對方替代。
兩者是截然不同的工具,但是恰巧都能夠達到部分功能,所以容易被混淆:
Gulp像是一個流水線,整個產品從無到有,都要受流水線的控制,在流水線上我們可以對產品進行管理
Gulp 是 task runner 任務管理工具。
目的:提供自動化與流程管理,整合前端開發環境,藉由簡化工作量,可讓開發者將重點放在功能的開發上。
功能:提供自訂任務流程,例如 babel、scss、壓縮、重新整理、校正時間等。
指令 | 解釋 |
---|---|
gulp.task() | 執行工作 |
gulp.src() | 檔案來源 |
gulp.dest() | 處理後的檔案輸出位置 |
gulp.pipe() | 串流來源檔案到另個外掛 |
gulp.watch () | 監控任務,當檔案有更動時,便會執行相對應的任務 |
在 src 和 dest 之間加入這些 plugin,可以幫助我們建構自動化工作流程。
|插件 | 用途 |
| -------- | -------- |
|gulp-babel |轉換語法|
|gulp-sass |將 sass 轉換成 css|
|gulp-htmlmin|壓縮 HTML|
|gulp-clean-css|壓縮 CSS|
|gulp-uglify|壓縮 javascript|
|gulp-imagemin|壓縮圖片|
|gulp-concat|合併多個檔案|
|browser-sync|瀏覽器同步檢視|
|gulp-ejs|動態生出html|
Webpack是一個模組打包機,可以將許多鬆散的模組按照依賴和規則打包,然後在瀏覽器上運行,解決舊瀏覽器不支援部分新語法的問題,也利於後續管理與維護。
透過loader的轉換,任何形式的資源都可以是做模組。 -- 一切皆模組啦!!!
]]>gulp 是 task runner 任務管理工具。
Webpack 是 module bundler 模組整合工具。
Webpack 和 gulp 兩個是不重疊的,他們本身都有 95% 的功能是不能被對方替代。
兩者是截然不同的工具,但是恰巧都能夠達到部分功能,所以容易被混淆:
Gulp像是一個流水線,整個產品從無到有,都要受流水線的控制,在流水線上我們可以對產品進行管理
Gulp 是 task runner 任務管理工具。
目的:提供自動化與流程管理,整合前端開發環境,藉由簡化工作量,可讓開發者將重點放在功能的開發上。
功能:提供自訂任務流程,例如 babel、scss、壓縮、重新整理、校正時間等。
指令 | 解釋 |
---|---|
gulp.task() | 執行工作 |
gulp.src() | 檔案來源 |
gulp.dest() | 處理後的檔案輸出位置 |
gulp.pipe() | 串流來源檔案到另個外掛 |
gulp.watch () | 監控任務,當檔案有更動時,便會執行相對應的任務 |
在 src 和 dest 之間加入這些 plugin,可以幫助我們建構自動化工作流程。
|插件 | 用途 |
| -------- | -------- |
|gulp-babel |轉換語法|
|gulp-sass |將 sass 轉換成 css|
|gulp-htmlmin|壓縮 HTML|
|gulp-clean-css|壓縮 CSS|
|gulp-uglify|壓縮 javascript|
|gulp-imagemin|壓縮圖片|
|gulp-concat|合併多個檔案|
|browser-sync|瀏覽器同步檢視|
|gulp-ejs|動態生出html|
Webpack是一個模組打包機,可以將許多鬆散的模組按照依賴和規則打包,然後在瀏覽器上運行,解決舊瀏覽器不支援部分新語法的問題,也利於後續管理與維護。
透過loader的轉換,任何形式的資源都可以是做模組。 -- 一切皆模組啦!!!
]]>剛開始學習前端時,都只會撰寫簡單的小專案,撰寫的檔案也相對單純,就是簡單的js檔案、css檔案等。當你要寫的檔案越來越大,為了管理或撰寫方便,你可能就會開始使用很多新的寫法。可是這些新的寫法,我們的瀏覽器是看不懂的,這時候我們就需要 Webpack 。
Webpack 幫我們整理所有的檔案並轉譯成瀏覽器看得懂的程式碼
不知道Gulp的看著 讓專案使用 Gulp 自動化編譯
這樣一講我又把gulp和webpack搞在一起了,到底兩個有什麼關係呢?
透過loaders把瀏覽器看不懂的檔案,整理成可以使用的檔案。
平常使用webpack建立一個簡單的專案時,就可以透過webpack先設定要使用哪種寫法(pug/scss等等),告訴webpack你要指定那些loaders,它就會幫你轉換啦!
當你修改某一處並儲存後,網頁不會整個重新reload,它只會重新執行你修改的地方
Entry file
build
指令
webpack 基礎套件
bundle
剛開始學習前端時,都只會撰寫簡單的小專案,撰寫的檔案也相對單純,就是簡單的js檔案、css檔案等。當你要寫的檔案越來越大,為了管理或撰寫方便,你可能就會開始使用很多新的寫法。可是這些新的寫法,我們的瀏覽器是看不懂的,這時候我們就需要 Webpack 。
Webpack 幫我們整理所有的檔案並轉譯成瀏覽器看得懂的程式碼
不知道Gulp的看著 讓專案使用 Gulp 自動化編譯
這樣一講我又把gulp和webpack搞在一起了,到底兩個有什麼關係呢?
透過loaders把瀏覽器看不懂的檔案,整理成可以使用的檔案。
平常使用webpack建立一個簡單的專案時,就可以透過webpack先設定要使用哪種寫法(pug/scss等等),告訴webpack你要指定那些loaders,它就會幫你轉換啦!
當你修改某一處並儲存後,網頁不會整個重新reload,它只會重新執行你修改的地方
Entry file
build
指令
webpack 基礎套件
bundle
Props 是從父層傳遞到內層內件的方式
而 emit 就是由內層往外層的方式啦!
假設今天有一個元件,元件內包含一個觸發的按鈕,按下去後需要觸發外層的其他東西,就需要使用emit將觸發傳出去!!
內層元件有個incrementCounter
的觸發事件!
我們先在內層寫入incrementCounter
的methods,
incrementCounter
的這個內層觸發事件,觸發後要影響外層,就需要在內層的methods內寫,
<div id="app">
<h2>透過 emit 向外傳遞資訊</h2>
我透過元件儲值了 {{ cash }} 元
<button-counter @increment="incrementTotal"></button-counter>
<hr>
<button-counter></button-counter>
</div>
Props 是從父層傳遞到內層內件的方式
而 emit 就是由內層往外層的方式啦!
假設今天有一個元件,元件內包含一個觸發的按鈕,按下去後需要觸發外層的其他東西,就需要使用emit將觸發傳出去!!
內層元件有個incrementCounter
的觸發事件!
我們先在內層寫入incrementCounter
的methods,
incrementCounter
的這個內層觸發事件,觸發後要影響外層,就需要在內層的methods內寫,
<div id="app">
<h2>透過 emit 向外傳遞資訊</h2>
我透過元件儲值了 {{ cash }} 元
<button-counter @increment="incrementTotal"></button-counter>
<hr>
<button-counter></button-counter>
</div>
props 由外面向元件內傳遞資料,又分兩種方式: 靜態傳遞 / 動態傳遞
<div id="app">
//動態傳遞(v-bind)
<greet-component :name="person1"></greet-component>
<greet-component :name="person2"></greet-component>
<greet-component v-for="(each,key) in personArray" :name="each" :key="key"></greet-component>
//靜態傳遞
<greet-component name="Andy"></greet-component>
</div>
const app = Vue.createApp({
data(){
return{
//使用data帶入
person1: 'Jake',
person2: 'Ted',
personArray: ['one','two','three']
}
}
});
//props 是將外部的資料傳入到元件內
//Global Component
app.component('greet-component',{
props: ['name'], //需要外部傳入資料
template: `
Hello {{name}}
`
})
app.mount('#app')
這邊要注意的是,我們在命名的時候:
props裡面命名使用小駝峰方式(imgUrl / isPublished / commentIds)
當在HTML上定義資料時不同單字要隔開(img-url / is-published / comment-ids)
我們在處理props時,要盡量維持單向數據流的觀念,不要反向的去修改
若要讓他返向修改資料,需要新定義一個變數來放新資料
<div id="app">
<photo :img-url="url"></photo>
</div>
const app = Vue.createApp({
data(){
return{
url: 'https://images.unsplash.com/photo-1522204538344-922f76ecc041?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=50e38600a12d623a878983fc5524423f&auto=format&fit=crop&w=1351&q=80',
}
}
});
//Global Component
app.component('photo',{
props: ['imgUrl'],
template: `
<div>
<img :src="imgUrl" class="img-fluid" alt="" />
<input type="text" class="form-control" v-model="imgUrl">
</div>
`,
data(){
return{
newUrl: this.imgUrl //這是props所傳進來的
}
}
})
app.mount('#app')
當我們要抓取遠端的資料時,可以使用v-if判斷資料內的某個東西有沒有存在,有的話在進行繪製HTML,不然在尚未抓完資料前就繪製出,會導致尚未宣告的錯誤。
如果你的資料匯入會有一些時間差,可子使用v-if將呈現往後移。
當不希望元件整個重新生成的話可以加上 keep-alive
props 由外面向元件內傳遞資料,又分兩種方式: 靜態傳遞 / 動態傳遞
<div id="app">
//動態傳遞(v-bind)
<greet-component :name="person1"></greet-component>
<greet-component :name="person2"></greet-component>
<greet-component v-for="(each,key) in personArray" :name="each" :key="key"></greet-component>
//靜態傳遞
<greet-component name="Andy"></greet-component>
</div>
const app = Vue.createApp({
data(){
return{
//使用data帶入
person1: 'Jake',
person2: 'Ted',
personArray: ['one','two','three']
}
}
});
//props 是將外部的資料傳入到元件內
//Global Component
app.component('greet-component',{
props: ['name'], //需要外部傳入資料
template: `
Hello {{name}}
`
})
app.mount('#app')
這邊要注意的是,我們在命名的時候:
props裡面命名使用小駝峰方式(imgUrl / isPublished / commentIds)
當在HTML上定義資料時不同單字要隔開(img-url / is-published / comment-ids)
我們在處理props時,要盡量維持單向數據流的觀念,不要反向的去修改
若要讓他返向修改資料,需要新定義一個變數來放新資料
<div id="app">
<photo :img-url="url"></photo>
</div>
const app = Vue.createApp({
data(){
return{
url: 'https://images.unsplash.com/photo-1522204538344-922f76ecc041?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=50e38600a12d623a878983fc5524423f&auto=format&fit=crop&w=1351&q=80',
}
}
});
//Global Component
app.component('photo',{
props: ['imgUrl'],
template: `
<div>
<img :src="imgUrl" class="img-fluid" alt="" />
<input type="text" class="form-control" v-model="imgUrl">
</div>
`,
data(){
return{
newUrl: this.imgUrl //這是props所傳進來的
}
}
})
app.mount('#app')
當我們要抓取遠端的資料時,可以使用v-if判斷資料內的某個東西有沒有存在,有的話在進行繪製HTML,不然在尚未抓完資料前就繪製出,會導致尚未宣告的錯誤。
如果你的資料匯入會有一些時間差,可子使用v-if將呈現往後移。
當不希望元件整個重新生成的話可以加上 keep-alive
// Create a Vue application
const app = Vue.createApp({});
// Define a new global component called button-counter
app.component('button-counter', {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`
})
app.mount('#app');
這個就需要了解遺下 ES6 的模組使用
import / export
下面兩篇文章皆是用 Single File Component
檔案: component.vue
<template lang="pug"> //可以設定想要用其他syntax
p{{greet}}
other-component
</template>
<script>
import OtherComponent from "./OtherComponent.vue"
export default {
component:{
OtherComponent
},
data(){
return{ greet: "Hello!"}
}
}
</script>
<style scoped>
//scoped設定只會套用到現在的component
//也可以加 lang="scss" 來設定寫法
p{
color: red;
}
</style>
]]>// Create a Vue application
const app = Vue.createApp({});
// Define a new global component called button-counter
app.component('button-counter', {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`
})
app.mount('#app');
這個就需要了解遺下 ES6 的模組使用
import / export
下面兩篇文章皆是用 Single File Component
檔案: component.vue
<template lang="pug"> //可以設定想要用其他syntax
p{{greet}}
other-component
</template>
<script>
import OtherComponent from "./OtherComponent.vue"
export default {
component:{
OtherComponent
},
data(){
return{ greet: "Hello!"}
}
}
</script>
<style scoped>
//scoped設定只會套用到現在的component
//也可以加 lang="scss" 來設定寫法
p{
color: red;
}
</style>
]]>
可以看看這篇文章 - Vue 元件 : Props in & Emit out
簡單來說整個網站就是多的元件組在一起變成一個大元件!
當我們建立多個元件後,我們需要先將單一元件註冊出來,再import進最主要呈現的App Component上。
解設,我們有一個元件為Greet.vue
,需要讓他與 App.vue
組起來呈現。
第一步就是要 export Greet.vue
在Greet.vue
內:
<template>
<h2>hello world</h2>
</template>
<script>
export default{
name: "Greet",
};
</script>
接下來就是在 App.vue
import Greet.vue
在App.vue
內:
<template>
<Greet></Greet>
<Greet /> //-- 如果在Greet中沒有其他內容,可以直接這樣寫
</template>
<script>
import Greet from 'path to Greet.vue'
export default{
name: "App",
component:{
Greet, //這邊也需要加上
}
};
</script>
]]>可以看看這篇文章 - Vue 元件 : Props in & Emit out
簡單來說整個網站就是多的元件組在一起變成一個大元件!
當我們建立多個元件後,我們需要先將單一元件註冊出來,再import進最主要呈現的App Component上。
解設,我們有一個元件為Greet.vue
,需要讓他與 App.vue
組起來呈現。
第一步就是要 export Greet.vue
在Greet.vue
內:
<template>
<h2>hello world</h2>
</template>
<script>
export default{
name: "Greet",
};
</script>
接下來就是在 App.vue
import Greet.vue
在App.vue
內:
<template>
<Greet></Greet>
<Greet /> //-- 如果在Greet中沒有其他內容,可以直接這樣寫
</template>
<script>
import Greet from 'path to Greet.vue'
export default{
name: "App",
component:{
Greet, //這邊也需要加上
}
};
</script>
]]>
prop 是即時傳入元件;emit 需要觸發才會傳入 side
元件的一個很大特點就是:
元件可以重複運用,而且不會互相影響
範例: 現在有App.vue
元件 與Greet.vue
元件,我們將Greet.vue
引入到App.vue
內,讓它被要多次使用,並且每次要與傳入的不同名字打招呼。
Greet.vue
內:<template>
<h2>Hello world</h2>
</template>
<script>
export default{
name: "Greet",
};
</script>
App.vue
內:<template>
<Greet />
<Greet />
<Greet />
</template>
<script>
import Greet form 'path to Greet.vue'
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱
}
};
</script>
現在我們不想要當它重複時就只是 Hello world,我們想要把人名傳進資料,跟每個人打招呼
要怎麼做呢?
步驟: 從 App.vue
傳送名字資料到 Greet.vue
,然後呈現在網頁上出名字
App.vue
內:首先我們在 Component上 放上自訂的資料屬性
<template>
<Greet person="Bruce" role="Teacher"/>
<Greet person="Clark" role="Doctor"/>
<Greet person="Dlana" role="Professor"/>
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
}
};
</script>
Greet.vue
內:那我們要如何讓Greet.vue
拿到資料呢?
使用props幫元件開一扇門來拿取變數。
<template>
<h2>Hello {{ role }} {{ person }}</h2>
</template>
<script>
export default{
name: "Greet", // 輸出的元件名稱
props: ['person','role'] //輸出的屬性資料
};
</script>
上面的方法,是直接將資料帶入自訂屬性
我們也可以引入 data資料 或 computed資料。
App.vue
內:<template>
<Greet :person="person" :role="role"/> //這邊需要使用bind加入
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
},
data(){
return{
person: 'Andy',
role: 'Manager',
}
}
};
</script>
props 是將資料從外面傳入到裡面,內層元件使用Props接收。
如果要從裡面傳出去到外面,就要使用Emit。
延續前面的情境:
Greet Component 內有一個按鈕,點擊按鈕後會讓外層 App Component 的資料做更動。
(在內層觸發事件,想要影響外層的值。)
Greet.vue
內:<template>
<h2>Hello {{ role }} {{ person }}</h2>
<button @click="$emit('enlarge-text')">字體放大<button>
</template>
<script>
export default{
name: "Greet", // 輸出的元件名稱
props: ['person','role'] //接收資料口
emits: ['enlarge-text'] // 送出的資料口(客製化事件)
};
</script>
App.vue
內:<template>
<div :style="{fontsize: fontsize+'em'}">
<Greet :person="person" :role="role" @enlarge-text="fontsize += 0.1"/>
</div>
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
},
data(){
return{
person: 'Andy',
role: 'Manager',
fontsize: 1,
}
}
};
</script>
Greet.vue
內:<template>
<h2>Hello {{ role }} {{ person }}</h2>
<button @click="$emit('enlarge-text',0.1)">字體放大<button>
//$emit('客製事件',參數(變數))
</template>
<script>
export default{
name: "Greet", // 輸出的元件名稱
props: ['person','role'] //接收資料口
emits: ['enlarge-text'] // 送出的資料口(客製化事件)
};
</script>
App.vue
內:<template>
<div :style="{fontsize: fontsize+'em'}">
<Greet :person="person" :role="role" @enlarge-text="fontsize += $event"/>
//使用 $event 接收內層客製事件傳出的變數!!
</div>
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
},
data(){
return{
person: 'Andy',
role: 'Manager',
fontsize: 1,
}
}
};
</script>
<template>
<div :style="{fontsize: fontsize+'em'}">
<Greet :person="person" :role="role" @enlarge-text="onEnlargeText"/>
//使用 $event 接收內層客製事件傳出的變數!!
</div>
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
},
data(){
return{
person: 'Andy',
role: 'Manager',
fontsize: 1,
}
},
methods:{
onEnlargeText(enlargeAmount){
this,fontsize += enlargeAmount
}
}
};
</script>
]]>prop 是即時傳入元件;emit 需要觸發才會傳入 side
元件的一個很大特點就是:
元件可以重複運用,而且不會互相影響
範例: 現在有App.vue
元件 與Greet.vue
元件,我們將Greet.vue
引入到App.vue
內,讓它被要多次使用,並且每次要與傳入的不同名字打招呼。
Greet.vue
內:<template>
<h2>Hello world</h2>
</template>
<script>
export default{
name: "Greet",
};
</script>
App.vue
內:<template>
<Greet />
<Greet />
<Greet />
</template>
<script>
import Greet form 'path to Greet.vue'
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱
}
};
</script>
現在我們不想要當它重複時就只是 Hello world,我們想要把人名傳進資料,跟每個人打招呼
要怎麼做呢?
步驟: 從 App.vue
傳送名字資料到 Greet.vue
,然後呈現在網頁上出名字
App.vue
內:首先我們在 Component上 放上自訂的資料屬性
<template>
<Greet person="Bruce" role="Teacher"/>
<Greet person="Clark" role="Doctor"/>
<Greet person="Dlana" role="Professor"/>
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
}
};
</script>
Greet.vue
內:那我們要如何讓Greet.vue
拿到資料呢?
使用props幫元件開一扇門來拿取變數。
<template>
<h2>Hello {{ role }} {{ person }}</h2>
</template>
<script>
export default{
name: "Greet", // 輸出的元件名稱
props: ['person','role'] //輸出的屬性資料
};
</script>
上面的方法,是直接將資料帶入自訂屬性
我們也可以引入 data資料 或 computed資料。
App.vue
內:<template>
<Greet :person="person" :role="role"/> //這邊需要使用bind加入
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
},
data(){
return{
person: 'Andy',
role: 'Manager',
}
}
};
</script>
props 是將資料從外面傳入到裡面,內層元件使用Props接收。
如果要從裡面傳出去到外面,就要使用Emit。
延續前面的情境:
Greet Component 內有一個按鈕,點擊按鈕後會讓外層 App Component 的資料做更動。
(在內層觸發事件,想要影響外層的值。)
Greet.vue
內:<template>
<h2>Hello {{ role }} {{ person }}</h2>
<button @click="$emit('enlarge-text')">字體放大<button>
</template>
<script>
export default{
name: "Greet", // 輸出的元件名稱
props: ['person','role'] //接收資料口
emits: ['enlarge-text'] // 送出的資料口(客製化事件)
};
</script>
App.vue
內:<template>
<div :style="{fontsize: fontsize+'em'}">
<Greet :person="person" :role="role" @enlarge-text="fontsize += 0.1"/>
</div>
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
},
data(){
return{
person: 'Andy',
role: 'Manager',
fontsize: 1,
}
}
};
</script>
Greet.vue
內:<template>
<h2>Hello {{ role }} {{ person }}</h2>
<button @click="$emit('enlarge-text',0.1)">字體放大<button>
//$emit('客製事件',參數(變數))
</template>
<script>
export default{
name: "Greet", // 輸出的元件名稱
props: ['person','role'] //接收資料口
emits: ['enlarge-text'] // 送出的資料口(客製化事件)
};
</script>
App.vue
內:<template>
<div :style="{fontsize: fontsize+'em'}">
<Greet :person="person" :role="role" @enlarge-text="fontsize += $event"/>
//使用 $event 接收內層客製事件傳出的變數!!
</div>
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
},
data(){
return{
person: 'Andy',
role: 'Manager',
fontsize: 1,
}
}
};
</script>
<template>
<div :style="{fontsize: fontsize+'em'}">
<Greet :person="person" :role="role" @enlarge-text="onEnlargeText"/>
//使用 $event 接收內層客製事件傳出的變數!!
</div>
</template>
<script>
import Greet form 'path to Greet.vue' //載入元件
export default{
name: "App",
component:{
Greet, //這邊需要放上元件名稱 //輸出的App元件中,所包含的元件
},
data(){
return{
person: 'Andy',
role: 'Manager',
fontsize: 1,
}
},
methods:{
onEnlargeText(enlargeAmount){
this,fontsize += enlargeAmount
}
}
};
</script>
]]>
在生命週期中,Vue.js 提供給開發者在每個週期階段可以做對應處理的 CallBack Function,這些 CallBack Function 稱為 Hooks Function。
vue3的composition已經不需要使用beforeCreate和created,但如果真的需要,可以在setup的區段內外來處理
Hooks Function | 在Vue3 composition API 的對應名稱 |
說明 |
---|---|---|
beforeCreate() | setup() | Vue初始化時期 這邊的動作會在vue導入期就被執行 此階段還拿不到Vue Data 也沒有辦法使用vue裡面的Methods, Watch, Computed等。 |
created() | setup() | Vue已被建立 可以拿到Vue Data, Function, Watch, Computed...等 但網頁內容HTML還沒準備好) |
beforeMount() | onBeforeMount() | 已經載入原始HTML至Virtual DOM 但內容尚未透過Vue進行渲染。 |
mounted() | onMounted() | 已經透過Vue進行渲染HTML 並且取代原本的元素內容。 這個時候可以進行一些HTML的操作 |
beforeUpdate() | onBeforeUpdate() | 當狀態被變動時,畫面同步更新前 |
updated() | onUpdated() | 當狀態被變動時,畫面同步更新完成 |
beforeDestroy()-2.x | x | Vue實體被銷毀前 |
beforeUnmount()-3.0 | onBeforeUnmount() | Vue實體被銷毀前 |
destoryed()-2.x | x | Vue實體被銷毀完畢 |
unmounted()-3.0 | onMounted() | Vue實體被銷毀完畢 |
errorCaptured() | onErrorCaptured() | 子/孫元件的錯誤被捕獲時觸發 |
activated() | x | Vue元件被啟動觸發時,須搭配keep-alive |
deactivated() | x | Vue元件被解除觸發時,須搭配keep-alive |
methods
屬性內,且由於需要透過this
存取實體,所以與methods
一樣不能使用箭頭函式。created
後才能使用<keep-alive>
在生命週期中,Vue.js 提供給開發者在每個週期階段可以做對應處理的 CallBack Function,這些 CallBack Function 稱為 Hooks Function。
vue3的composition已經不需要使用beforeCreate和created,但如果真的需要,可以在setup的區段內外來處理
Hooks Function | 在Vue3 composition API 的對應名稱 |
說明 |
---|---|---|
beforeCreate() | setup() | Vue初始化時期 這邊的動作會在vue導入期就被執行 此階段還拿不到Vue Data 也沒有辦法使用vue裡面的Methods, Watch, Computed等。 |
created() | setup() | Vue已被建立 可以拿到Vue Data, Function, Watch, Computed...等 但網頁內容HTML還沒準備好) |
beforeMount() | onBeforeMount() | 已經載入原始HTML至Virtual DOM 但內容尚未透過Vue進行渲染。 |
mounted() | onMounted() | 已經透過Vue進行渲染HTML 並且取代原本的元素內容。 這個時候可以進行一些HTML的操作 |
beforeUpdate() | onBeforeUpdate() | 當狀態被變動時,畫面同步更新前 |
updated() | onUpdated() | 當狀態被變動時,畫面同步更新完成 |
beforeDestroy()-2.x | x | Vue實體被銷毀前 |
beforeUnmount()-3.0 | onBeforeUnmount() | Vue實體被銷毀前 |
destoryed()-2.x | x | Vue實體被銷毀完畢 |
unmounted()-3.0 | onMounted() | Vue實體被銷毀完畢 |
errorCaptured() | onErrorCaptured() | 子/孫元件的錯誤被捕獲時觸發 |
activated() | x | Vue元件被啟動觸發時,須搭配keep-alive |
deactivated() | x | Vue元件被解除觸發時,須搭配keep-alive |
methods
屬性內,且由於需要透過this
存取實體,所以與methods
一樣不能使用箭頭函式。created
後才能使用<keep-alive>
sort()
可以使用 .charCodeAt(0)
查看
const letters = ['a','c','p','b'];
const newLetters = letters.sort();
console.log(newLetters)
//這邊就會得出依照 Unicode編碼排序的陣列 ["a","b","c","p"]
特別注意的是,大小寫是會造成不一樣的排列順序的!
如果今天是 ['a','A','c','b','D'] 出來會是 ['A','D','a','b','c']
letters.forEach(letter => console.log(letter, letter.charCodeAt(0)))
const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort();
console.log(newNumbers)
這邊可能一開始會想說 應該就是照著數字大小排列了吧
但是出來的結果卻是 [100,2,3,4,5,8]
讓我們使用 forEach 把每個都拿出來看看吧!
要注意的是,如果是數字的話,我們會需要先將它變為字串喔!
numbers.forEach(number => console.log(number, String(number).charCodeAt(0)))
這個時候就需要自己將函式寫進去啦!
首先,我們需要先帶入兩個參數 a,b。這兩個參數會做比較(大於、小於、等於)Three-Way Comparison。
如果:
a<b
: 如果 a 小於 b,a 會放在前面a>b
: 如果 a 大於 b,a 會放在後面a=b
: 如果 a 等於 b,a 位置不變const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort( (a,b) => {
if(a<b){
return -1
}else if(a>b){
return 1
}else{
return 0
}
});
console.log(newNumbers)
sort()會去看回傳的是 -1 、 1 還是 0,反著回去看:
回傳 -1
代表: 前面的數字小於後面的數字, a 放 b 前面
回傳 1
代表: 前面的數字大於後面的數字, b 放 a 前面
回傳 0
代表: 前面的數字等於後面的數字,所以不變
const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort( (a,b) => {
if(a>b){
return -1
}else if(a<b){
return 1
}else{
return 0
}
});
console.log(newNumbers)
sort()會去看回傳的是 -1 、 1 還是 0,反著回去看:
回傳 -1
代表: 前面的數字大於後面的數字, a 放 b 前面
回傳 1
代表: 前面的數字小於後面的數字, b 放 a 前面
回傳 0
代表: 前面的數字等於後面的數字,不變
看到前面落落長,我們是可以縮寫的!
其實回傳是 -1 、 1 或 0 並不是重點,
當我們的參數(a,b)比較
只要是比較後回傳負值
,a就放前面。
只要是比較後回傳正值
,a就放後面。
只要是比較後回傳0
,不變。
所以就有了縮寫寫法:
直接寫出
const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort( (a,b) => {
return a-b
//a-b 若是負數(代表a<b),負數會讓a放前面,就會呈現正序
});
console.log(newNumbers)
const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort( (a,b) => {
return b-a
//b-a 若是負數(代表a>b),負數會讓a放前面,就會呈現倒序
});
console.log(newNumbers)
]]>sort()
可以使用 .charCodeAt(0)
查看
const letters = ['a','c','p','b'];
const newLetters = letters.sort();
console.log(newLetters)
//這邊就會得出依照 Unicode編碼排序的陣列 ["a","b","c","p"]
特別注意的是,大小寫是會造成不一樣的排列順序的!
如果今天是 ['a','A','c','b','D'] 出來會是 ['A','D','a','b','c']
letters.forEach(letter => console.log(letter, letter.charCodeAt(0)))
const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort();
console.log(newNumbers)
這邊可能一開始會想說 應該就是照著數字大小排列了吧
但是出來的結果卻是 [100,2,3,4,5,8]
讓我們使用 forEach 把每個都拿出來看看吧!
要注意的是,如果是數字的話,我們會需要先將它變為字串喔!
numbers.forEach(number => console.log(number, String(number).charCodeAt(0)))
這個時候就需要自己將函式寫進去啦!
首先,我們需要先帶入兩個參數 a,b。這兩個參數會做比較(大於、小於、等於)Three-Way Comparison。
如果:
a<b
: 如果 a 小於 b,a 會放在前面a>b
: 如果 a 大於 b,a 會放在後面a=b
: 如果 a 等於 b,a 位置不變const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort( (a,b) => {
if(a<b){
return -1
}else if(a>b){
return 1
}else{
return 0
}
});
console.log(newNumbers)
sort()會去看回傳的是 -1 、 1 還是 0,反著回去看:
回傳 -1
代表: 前面的數字小於後面的數字, a 放 b 前面
回傳 1
代表: 前面的數字大於後面的數字, b 放 a 前面
回傳 0
代表: 前面的數字等於後面的數字,所以不變
const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort( (a,b) => {
if(a>b){
return -1
}else if(a<b){
return 1
}else{
return 0
}
});
console.log(newNumbers)
sort()會去看回傳的是 -1 、 1 還是 0,反著回去看:
回傳 -1
代表: 前面的數字大於後面的數字, a 放 b 前面
回傳 1
代表: 前面的數字小於後面的數字, b 放 a 前面
回傳 0
代表: 前面的數字等於後面的數字,不變
看到前面落落長,我們是可以縮寫的!
其實回傳是 -1 、 1 或 0 並不是重點,
當我們的參數(a,b)比較
只要是比較後回傳負值
,a就放前面。
只要是比較後回傳正值
,a就放後面。
只要是比較後回傳0
,不變。
所以就有了縮寫寫法:
直接寫出
const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort( (a,b) => {
return a-b
//a-b 若是負數(代表a<b),負數會讓a放前面,就會呈現正序
});
console.log(newNumbers)
const numbers=[2,5,8,3,4,100];
const newNumbers = numbers.sort( (a,b) => {
return b-a
//b-a 若是負數(代表a>b),負數會讓a放前面,就會呈現倒序
});
console.log(newNumbers)
]]>
v-on:click="rotate";
@click="rotote"
@click="addMoney(item)"
.prevent
.stop
等同於event.stopPropagation(),防止事件冒泡。
.capture
與事件冒泡的方向相反,事件捕獲 (event capturing) 是由外而內的。
.once
只會執行一次。
.self
只會觸發自己範圍內的事件,不包含子元素
@keyup.shift.enter="觸發事件"
@click.right
@click.left
@click.middle
v-on:click="rotate";
@click="rotote"
@click="addMoney(item)"
.prevent
.stop
等同於event.stopPropagation(),防止事件冒泡。
.capture
與事件冒泡的方向相反,事件捕獲 (event capturing) 是由外而內的。
.once
只會執行一次。
.self
只會觸發自己範圍內的事件,不包含子元素
@keyup.shift.enter="觸發事件"
@click.right
@click.left
@click.middle
把畫面與資料綁在一起,需要有事件觸發(例如: click、keyup等等),觸發後會影響畫面與資料。
computed是一個計算屬性,只有在相關依賴發生改變時才會重新求值。
針對輸出到畫面的內容做處理,常用在(過濾)。
觸發的意思就是,幫我們要搜尋並過濾東西,需要在填好 輸入框 後,按下觸發按鈕,才會進行過濾與畫面渲染。
而 Computed 可以減少掉 觸發按鈕 這件事! 它只要發現 輸入框 的內容資料改變,就可以直接進行過濾與畫面渲染。
<input type="text" v-model="filterText">
<ul>
<li v-for="item in filterArray" :key="item.id">
{{item}}
</li>
</ul>
computed:{
filterArray: function(){
var vm = this; //當我們在內部進行filter時,this指向會跑調
return vm.arrayData.filter(function(item){
return item.name.match(vm.filterText)
//只要filterText 的 輸入欄 值 改變 就會直接觸發
})
}
}
若是遇到資料量很大時,computed的效能就會受到影響。
有時候後端會用穿入 timestampe 的方式傳入時間,我們就可以運用computed將時間數字串轉為時間格式。
範例: 當執行結束就會跑一次timestampe
{{newDate}}
<script>
computed:{
formatTime(){
console.log(this.newDate);
//因為 timestamp 取得的是秒數,但在 JavaScript 中要帶入的是毫秒,所以要 new Date(timestamp * 1000)
var dates = new Date(this.newDate * 1000);
var year = dates.getFullYear();
var month = dates.getMonth() + 1;
var date = dates.getDate() + 1;
var hours = dates.getHours();
var minutes = dates.getMinutes();
var seconds = dates.getSeconds();
return `${year}/${month}/${date}/${hours}/${minutes}/${seconds}`
}
},
mounted(){
//這邊要取得的是 timestamp
// (Date.now()取得的是毫秒數,如果要讓阿變成 timestamp 要除1000,因為TimeStamp 是以秒數呈現)
this.newDate = Math.floor(Date.now()/1000);
}
</script>
當特定變數產生變化時,會執行的function
Watch 真的覺得和 Computed 很難區分,其中不同的點:
watch 較適合做非同步的操做, computed 無法這樣操作。
( 非同步: watch 可以在監聽到特定變數改變時,在一段時間後執行setTimeout()
)
方法 | 比較 |
---|---|
methods | 必須要有一定的觸發條件才能執行 如點擊事件 |
computed | HTML DOM加載後馬上執行的 等到依賴資料改變時才會再次求值 |
watch | 適合做非同步的操做 |
把畫面與資料綁在一起,需要有事件觸發(例如: click、keyup等等),觸發後會影響畫面與資料。
computed是一個計算屬性,只有在相關依賴發生改變時才會重新求值。
針對輸出到畫面的內容做處理,常用在(過濾)。
觸發的意思就是,幫我們要搜尋並過濾東西,需要在填好 輸入框 後,按下觸發按鈕,才會進行過濾與畫面渲染。
而 Computed 可以減少掉 觸發按鈕 這件事! 它只要發現 輸入框 的內容資料改變,就可以直接進行過濾與畫面渲染。
<input type="text" v-model="filterText">
<ul>
<li v-for="item in filterArray" :key="item.id">
{{item}}
</li>
</ul>
computed:{
filterArray: function(){
var vm = this; //當我們在內部進行filter時,this指向會跑調
return vm.arrayData.filter(function(item){
return item.name.match(vm.filterText)
//只要filterText 的 輸入欄 值 改變 就會直接觸發
})
}
}
若是遇到資料量很大時,computed的效能就會受到影響。
有時候後端會用穿入 timestampe 的方式傳入時間,我們就可以運用computed將時間數字串轉為時間格式。
範例: 當執行結束就會跑一次timestampe
{{newDate}}
<script>
computed:{
formatTime(){
console.log(this.newDate);
//因為 timestamp 取得的是秒數,但在 JavaScript 中要帶入的是毫秒,所以要 new Date(timestamp * 1000)
var dates = new Date(this.newDate * 1000);
var year = dates.getFullYear();
var month = dates.getMonth() + 1;
var date = dates.getDate() + 1;
var hours = dates.getHours();
var minutes = dates.getMinutes();
var seconds = dates.getSeconds();
return `${year}/${month}/${date}/${hours}/${minutes}/${seconds}`
}
},
mounted(){
//這邊要取得的是 timestamp
// (Date.now()取得的是毫秒數,如果要讓阿變成 timestamp 要除1000,因為TimeStamp 是以秒數呈現)
this.newDate = Math.floor(Date.now()/1000);
}
</script>
當特定變數產生變化時,會執行的function
Watch 真的覺得和 Computed 很難區分,其中不同的點:
watch 較適合做非同步的操做, computed 無法這樣操作。
( 非同步: watch 可以在監聽到特定變數改變時,在一段時間後執行setTimeout()
)
方法 | 比較 |
---|---|
methods | 必須要有一定的觸發條件才能執行 如點擊事件 |
computed | HTML DOM加載後馬上執行的 等到依賴資料改變時才會再次求值 |
watch | 適合做非同步的操做 |
v-if
v-else
可以用來做切換物件呈現、或是過濾滿足條件的呈現//寫法一
<div v-if=" isGirl === true ">是女生</div>
<div v-if=" isGirl === false ">是男生</div>
//寫法二
<div v-if="isGirl">是女生</div>
<div v-if="!isGirl">是男生</div>
//寫法三
<div v-if="isGirl">是女生</div>
<div v-else>是男生</div>
範例二 :
<li v-for="item in dataArray" v-if="age > 30">
{{item.name}}
</li>
我們可以透過v-if來切換內容呈現,例如下面表格範例:
我們可以透過 input-checkbox 來toggle上 showTemplate
的 true/false
最一開始設置為false讓它隱藏,再toggle上true讓表格顯示
<table>
<tr v-if="showTemplate">
<td>1</td>
<td>text1</td>
</tr>
<tr v-if="showTemplate">
<td>2</td>
<td>text2</td>
</tr>
</table>
這樣做的缺點是,我需要在每行上都加上 v-if="showTemplate"
為了讓他更簡單,我們可以改用 template 標籤
template 標籤 根據 MDN 的解釋 :
是用作保存用戶端內容的機制。該內容在頁面載入時不受渲染,但可以在運行時使用 JavaScript 實例化。
vue.js的好處呢!就是可以一樣使用template啦!
我們可以在template
標籤,放上vue的指令,來控制他的呈現。
<table>
<template v-if="showTemplate">
<tr>
<td>1</td>
<td>text1</td>
</tr>
<tr>
<td>2</td>
<td>text2</td>
</tr>
</template>
</table>
運用 v-if、v-else-if、v-else 時,一定要注意需要放置在一起,才能告知程式它有連帶關係
不管是在做 v-for
v-if
,在使用 Vue 時 key
非常的重要!
在運作時,當切換的畫面資料中,有相同的元件,Vue 就會以快速置換(非常高效能)的方式切換,但是這樣的快速切換往往會導致我們的資料跑掉,所以我們才會需要補上 唯一 KEY 值。
當KEY值不同時,就會進行重新渲染
v-if
會直接把動元素移除
<div class="box bgRed" v-if="isShowed">
<div class="box bgBlue" v-if="!isShowed">
當 isShowed === true 切換時:
<div class="box bgRed" v-if="isShowed">
<div class="box" v-if="!isShowed">
v-show
會透過加入和刪除 style="display: none;"
<div class="box bgRed" v-show="isShowed">
<div class="box bgBlue" v-show="!isShowed">
當 isShowed === true 切換時:
<div class="box bgRed" v-show="isShowed">
<div class="box bgBlue" v-show="!isShowed" style="display: none;">
]]>v-if
v-else
可以用來做切換物件呈現、或是過濾滿足條件的呈現//寫法一
<div v-if=" isGirl === true ">是女生</div>
<div v-if=" isGirl === false ">是男生</div>
//寫法二
<div v-if="isGirl">是女生</div>
<div v-if="!isGirl">是男生</div>
//寫法三
<div v-if="isGirl">是女生</div>
<div v-else>是男生</div>
範例二 :
<li v-for="item in dataArray" v-if="age > 30">
{{item.name}}
</li>
我們可以透過v-if來切換內容呈現,例如下面表格範例:
我們可以透過 input-checkbox 來toggle上 showTemplate
的 true/false
最一開始設置為false讓它隱藏,再toggle上true讓表格顯示
<table>
<tr v-if="showTemplate">
<td>1</td>
<td>text1</td>
</tr>
<tr v-if="showTemplate">
<td>2</td>
<td>text2</td>
</tr>
</table>
這樣做的缺點是,我需要在每行上都加上 v-if="showTemplate"
為了讓他更簡單,我們可以改用 template 標籤
template 標籤 根據 MDN 的解釋 :
是用作保存用戶端內容的機制。該內容在頁面載入時不受渲染,但可以在運行時使用 JavaScript 實例化。
vue.js的好處呢!就是可以一樣使用template啦!
我們可以在template
標籤,放上vue的指令,來控制他的呈現。
<table>
<template v-if="showTemplate">
<tr>
<td>1</td>
<td>text1</td>
</tr>
<tr>
<td>2</td>
<td>text2</td>
</tr>
</template>
</table>
運用 v-if、v-else-if、v-else 時,一定要注意需要放置在一起,才能告知程式它有連帶關係
不管是在做 v-for
v-if
,在使用 Vue 時 key
非常的重要!
在運作時,當切換的畫面資料中,有相同的元件,Vue 就會以快速置換(非常高效能)的方式切換,但是這樣的快速切換往往會導致我們的資料跑掉,所以我們才會需要補上 唯一 KEY 值。
當KEY值不同時,就會進行重新渲染
v-if
會直接把動元素移除
<div class="box bgRed" v-if="isShowed">
<div class="box bgBlue" v-if="!isShowed">
當 isShowed === true 切換時:
<div class="box bgRed" v-if="isShowed">
<div class="box" v-if="!isShowed">
v-show
會透過加入和刪除 style="display: none;"
<div class="box bgRed" v-show="isShowed">
<div class="box bgBlue" v-show="!isShowed">
當 isShowed === true 切換時:
<div class="box bgRed" v-show="isShowed">
<div class="box bgBlue" v-show="!isShowed" style="display: none;">
]]>
key 的用途就是用來辨識 virtual DOM 的更新情況,其中 virtual DOM 的特性之一就是會重複使用原本的組件,要更新時不會整個組件的 DOM 元素砍掉然後再全部渲染一次,而是只更新部分資料,這樣做可以提升效能。
若在v-for渲染的項目中沒有加入key,預設的行為是當數據順序改變時,vue會就地更新(In-place patch)進行變更,也就是說Vue不會移動任何DOM元素來配合數據順序,只是就地去更新項目內容確保他們在每個索引位置正確的渲染。
Key 建議使用 id 或是 唯一值
過濾 也是 v-for 常常會做到的功能
要過濾資料的話你可以這樣做:
filter(){
this.filterDataArray = this.dataArray.filter(
function(item){
利用 this.filterText 、 item物件內容 做過濾
}
)
}
<ul>
<li v-for="item in 10">{{item}}</li>
</ul>
表格與v-for
運用
<table class="table">
<template v-for=" item in arrayData">
<tr>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
</tr>
</template>
</table>
當兩個同時使用時,會先執行 v-for
再執行 v-if
<ul>
<li v-for="(item, key) in arrayData" v-if="item.age <= 20">
{{ key }} - {{ item.name }} {{ item.age }} 歲
{{ }}
</li>
<ul>
this.dataArray.length = 0
-> 出錯,畫面不會更改直接使用陣列索引的方式操作(修改)內容this.dataArray[0]={data: newdata;}
-> 出錯,畫面不會更改。(解決方法: Vue.set())
範例:
Vue.createApp({
data(){
dataArray:[
data1:{ name : 'abby'},
]
},
methods:{
//使用陣列索引的方式操作(修改)內容,不能運作
cantWorkEdit(){
this.dataArray[0]={
name: 'jake'
}
}
//解決方法 Vue.set()
Vue.set(this.dataArray, 0, 'jake')
}
})
當我們要操作的資料,本來不在data上,就需要用Vue.set()來將資料寫入data內。
使用Vue.set()強制將資料寫入
Vue.set(target, key, value)
//target: 要針對的目標
//key: 索引 or key
//value: 值(可以是物件)
key 的用途就是用來辨識 virtual DOM 的更新情況,其中 virtual DOM 的特性之一就是會重複使用原本的組件,要更新時不會整個組件的 DOM 元素砍掉然後再全部渲染一次,而是只更新部分資料,這樣做可以提升效能。
若在v-for渲染的項目中沒有加入key,預設的行為是當數據順序改變時,vue會就地更新(In-place patch)進行變更,也就是說Vue不會移動任何DOM元素來配合數據順序,只是就地去更新項目內容確保他們在每個索引位置正確的渲染。
Key 建議使用 id 或是 唯一值
過濾 也是 v-for 常常會做到的功能
要過濾資料的話你可以這樣做:
filter(){
this.filterDataArray = this.dataArray.filter(
function(item){
利用 this.filterText 、 item物件內容 做過濾
}
)
}
<ul>
<li v-for="item in 10">{{item}}</li>
</ul>
表格與v-for
運用
<table class="table">
<template v-for=" item in arrayData">
<tr>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
</tr>
</template>
</table>
當兩個同時使用時,會先執行 v-for
再執行 v-if
<ul>
<li v-for="(item, key) in arrayData" v-if="item.age <= 20">
{{ key }} - {{ item.name }} {{ item.age }} 歲
{{ }}
</li>
<ul>
this.dataArray.length = 0
-> 出錯,畫面不會更改直接使用陣列索引的方式操作(修改)內容this.dataArray[0]={data: newdata;}
-> 出錯,畫面不會更改。(解決方法: Vue.set())
範例:
Vue.createApp({
data(){
dataArray:[
data1:{ name : 'abby'},
]
},
methods:{
//使用陣列索引的方式操作(修改)內容,不能運作
cantWorkEdit(){
this.dataArray[0]={
name: 'jake'
}
}
//解決方法 Vue.set()
Vue.set(this.dataArray, 0, 'jake')
}
})
當我們要操作的資料,本來不在data上,就需要用Vue.set()來將資料寫入data內。
使用Vue.set()強制將資料寫入
Vue.set(target, key, value)
//target: 要針對的目標
//key: 索引 or key
//value: 值(可以是物件)
{{TEXT}}
使用v-html
插入
在網頁上直接寫入html是危險的,可能導致 XSS攻擊 (透過用戶端輸入框寫入HTML來攻擊),因此v-html 只能用在可信任的內容上,不要用在用戶提交的內容上
v-once
只會輸出一次,之後輸出不會更著綁定
{{ text1 + text2 }}
{{ number1 * number2 }}
{{ text.split('').reverse().join('')}}
v-bind:id=" "
:id=" "
:disabled =" isDisabled"
(true/false控制)
{{TEXT}}
使用v-html
插入
在網頁上直接寫入html是危險的,可能導致 XSS攻擊 (透過用戶端輸入框寫入HTML來攻擊),因此v-html 只能用在可信任的內容上,不要用在用戶提交的內容上
v-once
只會輸出一次,之後輸出不會更著綁定
{{ text1 + text2 }}
{{ number1 * number2 }}
{{ text.split('').reverse().join('')}}
v-bind:id=" "
:id=" "
:disabled =" isDisabled"
(true/false控制)
通常可以這樣使用:
<div :class="{'className' : condition}"> </div>
如果有一個以上可以利用逗號放在一起
<div :class="{'className1' : condition1},{'className2' : condition2}"> </div>
但是如果有非常多個呢? 我們可以用傳入物件的方式來操作!
使用此方法比較不同的是,之前會在 : 後放上條件,而全部寫在一個陣列時會直接寫上 true / false,要切換時再加上
特別注意: 若className中間有 - ,應該要以[]包起來 classObject[class-Name1]
div: @click="classObject.className1 == !classObject.className1"
input: v-model="classObject.className2"
<div :class="classObject"> </div>
<script>
Vue.createApp({
data(){
return{
//直接建立一個類別物件
classObject: {
'className1' : false
'className2' : false
},
}
}
})
</script>
上面的方式是以true和false的條件插入。
而使用陣列是可以讓不是單純用true和false條件來插入的Class
input: v-model="classArray" value="className1"
<div :class="classArray"> </div>
<script>
Vue.createApp({
data(){
return{
//直接建立一個類別物件
classArray: [
'className1',
'className2',
],
}
}
})
</script>
特別注意: 若Class間有 -,需要將第二個首字改大寫 backgroun-color > backgroundColor
使用物件方式插入
<div :style="{ backgroundColor : 'red' }"> </div>
將物件存為變數使用(以物件傳入)
<div :style="styleObject"> </div>
<script>
Vue.createApp({
data(){
return{
//直接建立一個類別物件
styleObject: {
backgroundColor: 'red',
borderWidth: '5px'
},
}
}
})
</script>
以陣列傳入
陣列的傳入方式,是將所有物件起來
<div :style="[{ backgroundColor : 'red' },{ borderWidth : '5px' }]"> </div>
<div :style="[ styleObject1 , styleObject2 ]"> </div>
用在表單上
<input type="text" :disabled="isdisabled" v-model="message" placeholder="輸入文字">
通常可以這樣使用:
<div :class="{'className' : condition}"> </div>
如果有一個以上可以利用逗號放在一起
<div :class="{'className1' : condition1},{'className2' : condition2}"> </div>
但是如果有非常多個呢? 我們可以用傳入物件的方式來操作!
使用此方法比較不同的是,之前會在 : 後放上條件,而全部寫在一個陣列時會直接寫上 true / false,要切換時再加上
特別注意: 若className中間有 - ,應該要以[]包起來 classObject[class-Name1]
div: @click="classObject.className1 == !classObject.className1"
input: v-model="classObject.className2"
<div :class="classObject"> </div>
<script>
Vue.createApp({
data(){
return{
//直接建立一個類別物件
classObject: {
'className1' : false
'className2' : false
},
}
}
})
</script>
上面的方式是以true和false的條件插入。
而使用陣列是可以讓不是單純用true和false條件來插入的Class
input: v-model="classArray" value="className1"
<div :class="classArray"> </div>
<script>
Vue.createApp({
data(){
return{
//直接建立一個類別物件
classArray: [
'className1',
'className2',
],
}
}
})
</script>
特別注意: 若Class間有 -,需要將第二個首字改大寫 backgroun-color > backgroundColor
使用物件方式插入
<div :style="{ backgroundColor : 'red' }"> </div>
將物件存為變數使用(以物件傳入)
<div :style="styleObject"> </div>
<script>
Vue.createApp({
data(){
return{
//直接建立一個類別物件
styleObject: {
backgroundColor: 'red',
borderWidth: '5px'
},
}
}
})
</script>
以陣列傳入
陣列的傳入方式,是將所有物件起來
<div :style="[{ backgroundColor : 'red' },{ borderWidth : '5px' }]"> </div>
<div :style="[ styleObject1 , styleObject2 ]"> </div>
用在表單上
<input type="text" :disabled="isdisabled" v-model="message" placeholder="輸入文字">
Slice 像是擷取一樣,透過slice你可以擷取陣列或字串中的一段,並將那一段複製起來放到新的陣列或字串。
slice(fromIndex, untilIndex);
//他會擷取到指定的索引前,也就是若我的untilIndex是3,他不會擷取到索引3的呦!
Splice()的作用是,可以將東西刪除再拼東西上去。
splice(fromIndex, remove number of elements)
splice(2) = 代表從索引2開始的東西都被刪除。(索引2也會被刪)
splice(2,1) = 代表從索引2開始,刪除一項東西。(也就是刪除索引2)
splice(2,3) = 代表從索引2開始,刪除三項東西。(也就是刪除索引2)
splice(fromIndex, remove number of elements, element, element)
splice(2,1,'a','b') = 代表從索引2開始,刪除一項東西,再拼上後面的東西('a','b')。
Split()會將一個字串,依照指定方式分裂成多個小字串,再將其餘集結成一個陣列回傳。
string.split(separator, limit);
//第一個是指定要怎麼分裂
//第二個是指定要分裂到哪裡
範例:
const str1 = "1,2,3,4,5,6,7"
const str2 = "hello,world"
str1.split(',',3); //return ['1','2','3']
//指定要把有','的地方都分裂開來,並分裂到第三個,然後組成一個陣列回傳。
str2.split('',5); //return ['h','e','l','l','o']
//如果是 '' 表示就是把每個字元都分裂開來
]]>Slice 像是擷取一樣,透過slice你可以擷取陣列或字串中的一段,並將那一段複製起來放到新的陣列或字串。
slice(fromIndex, untilIndex);
//他會擷取到指定的索引前,也就是若我的untilIndex是3,他不會擷取到索引3的呦!
Splice()的作用是,可以將東西刪除再拼東西上去。
splice(fromIndex, remove number of elements)
splice(2) = 代表從索引2開始的東西都被刪除。(索引2也會被刪)
splice(2,1) = 代表從索引2開始,刪除一項東西。(也就是刪除索引2)
splice(2,3) = 代表從索引2開始,刪除三項東西。(也就是刪除索引2)
splice(fromIndex, remove number of elements, element, element)
splice(2,1,'a','b') = 代表從索引2開始,刪除一項東西,再拼上後面的東西('a','b')。
Split()會將一個字串,依照指定方式分裂成多個小字串,再將其餘集結成一個陣列回傳。
string.split(separator, limit);
//第一個是指定要怎麼分裂
//第二個是指定要分裂到哪裡
範例:
const str1 = "1,2,3,4,5,6,7"
const str2 = "hello,world"
str1.split(',',3); //return ['1','2','3']
//指定要把有','的地方都分裂開來,並分裂到第三個,然後組成一個陣列回傳。
str2.split('',5); //return ['h','e','l','l','o']
//如果是 '' 表示就是把每個字元都分裂開來
]]>
今天我想自己練習來作一個超級簡易版小畫家,效果包含:
HTML Canvas 會提供一個畫布空間,需要使用到JavaScript來作畫。
Canvas為一個正方形的區塊,預設就是空白的、沒有border。
HTML:
<canvas id="myCanvas" width="200" height="100"></canvas>
JavaScript:
取得 Canvas 的渲染環境及其繪圖函數
const canvas = document.querySelector('#myCanvas');
const ctx = canvas.getContext('2d');
但放上去後發現就算更改的寬度和長度,還是無法讓畫布滿版、自適應整個螢幕。
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
前面提到 canvas 就是一個空白的畫布,填上東西靠的是content。
通常會宣告為ctx
。
我們可以先對ctx
最一些基礎的設定(例如: 筆刷大小/ 顏色等)
ctx.strokeStyle = '#bada55'; // 筆觸顏色
ctx.lineJoin = 'round'; // 兩條線交匯處產生 "圓形" 邊角
ctx.lineCap = 'round'; // 筆觸預設為 "圓形"
ctx.lineWidth = 1; // 筆頭寬度
let isDrawing = false; // 是否 mousedown下筆狀態
/* 起點座標 */
let lastX = 0;
let lastY = 0;
重點:
canvas.addEventListener('mouseup', () => isDrawing = false);
canvas.addEventListener('mouseout', () => isDrawing = false);
canvas.addEventListener('mousedown', (e) => {
isDrawing = true;
[lastX, lastY] = [e.offsetX, e.offsetY]; // 設定起始點
});
canvas.addEventListener('mousemove', draw);
// 繪製函數;在 mousemove 的時候使用
function draw(e) {
if(!isDrawing) return; // 沒有允許繪製即退出
/* 繪製路線 Setting */
ctx.beginPath(); // 產生一個新路徑,產生後再使用繪圖指令來設定路徑。
ctx.moveTo(lastX, lastY); // 設定起點
ctx.lineTo(e.offsetX, e.offsetY); // 設定終點
ctx.stroke(); // 依照設定開始繪製
[lastX, lastY] = [e.offsetX, e.offsetY]; // 位置更新
//當目前路徑為空(例如接著呼叫beginPath()完後)或是在一個新畫布上,不論為何,第一個路徑繪圖指令總是moveTo();因為每當重設路徑後,你幾乎都會需要設定繪圖起始點。
顏色使用 input type="color"
去監聽它的改變在動態寫入value
粗細的部分我已經寫好固定提供五個大小
五個圈圈搭被dataset,利用forEach 去監聽每個圈圈是否被點擊,點擊後動態寫入數值。
今天我想自己練習來作一個超級簡易版小畫家,效果包含:
HTML Canvas 會提供一個畫布空間,需要使用到JavaScript來作畫。
Canvas為一個正方形的區塊,預設就是空白的、沒有border。
HTML:
<canvas id="myCanvas" width="200" height="100"></canvas>
JavaScript:
取得 Canvas 的渲染環境及其繪圖函數
const canvas = document.querySelector('#myCanvas');
const ctx = canvas.getContext('2d');
但放上去後發現就算更改的寬度和長度,還是無法讓畫布滿版、自適應整個螢幕。
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
前面提到 canvas 就是一個空白的畫布,填上東西靠的是content。
通常會宣告為ctx
。
我們可以先對ctx
最一些基礎的設定(例如: 筆刷大小/ 顏色等)
ctx.strokeStyle = '#bada55'; // 筆觸顏色
ctx.lineJoin = 'round'; // 兩條線交匯處產生 "圓形" 邊角
ctx.lineCap = 'round'; // 筆觸預設為 "圓形"
ctx.lineWidth = 1; // 筆頭寬度
let isDrawing = false; // 是否 mousedown下筆狀態
/* 起點座標 */
let lastX = 0;
let lastY = 0;
重點:
canvas.addEventListener('mouseup', () => isDrawing = false);
canvas.addEventListener('mouseout', () => isDrawing = false);
canvas.addEventListener('mousedown', (e) => {
isDrawing = true;
[lastX, lastY] = [e.offsetX, e.offsetY]; // 設定起始點
});
canvas.addEventListener('mousemove', draw);
// 繪製函數;在 mousemove 的時候使用
function draw(e) {
if(!isDrawing) return; // 沒有允許繪製即退出
/* 繪製路線 Setting */
ctx.beginPath(); // 產生一個新路徑,產生後再使用繪圖指令來設定路徑。
ctx.moveTo(lastX, lastY); // 設定起點
ctx.lineTo(e.offsetX, e.offsetY); // 設定終點
ctx.stroke(); // 依照設定開始繪製
[lastX, lastY] = [e.offsetX, e.offsetY]; // 位置更新
//當目前路徑為空(例如接著呼叫beginPath()完後)或是在一個新畫布上,不論為何,第一個路徑繪圖指令總是moveTo();因為每當重設路徑後,你幾乎都會需要設定繪圖起始點。
顏色使用 input type="color"
去監聽它的改變在動態寫入value
粗細的部分我已經寫好固定提供五個大小
五個圈圈搭被dataset,利用forEach 去監聽每個圈圈是否被點擊,點擊後動態寫入數值。
var num ="123456444";
console.log(num.replace(/\B(?=(\d{3})+(?!\d))/g, ','))
var num ="123456444";
console.log(num.replace(/\B(?=(\d{3})+(?!\d))/g, ','))
在字串或段落中搜尋的一個方法,是用來描述字串符合某個語法規則的模型,可以用來做文字的搜尋、比對、萃取、替代、轉換等等。
可以使用兩個 / /
或是new RegExp()
來建立一個RegExp物件。
/
夾住條件const rule = /[a-z]/;
// [] character set; 像是一個搜尋範圍。
//matches a character in the range "a" to "z"
const regex = /some text/;
//使用literal斜線,這種方式會在script載入時就被編譯,效能較好
新增RegExp物件
var rules = new RegExp('[0-9]')
//matches a character in the range "0" to "9"
const regex = new RegExp('some text')
//使用 new 建構一個 RegExp 物件,適合用在需要動態產生 pattern 的場合。
加上flag
const regex = /some text/i ;
// i : 不區分大小寫
const regex = new RegExp('some text', 'g');
// g :比對字串所有位置
可以使用RegExp 物件中的tet
exec
,在String物件
中的search
、match
、replace
、split
等方法中,也有支援正規表達式寫法。
var str = 'happy';
// 驗證字串為 a~z 的字母
const rules = /[a-z]/;
console.log(rules.test(str));
// true
console.log(/\d/.test(str));
// false
// \d 的意思是: match any digit character (0-9)
const regex = /hello world/i
regex.exec('Hello World !!') // ["Hello World", index: 0, input: "Hello World !!", groups: undefined]
regex.exec('Hello Regex !!') // null ; 比對失敗時回傳 null
3.search()、match()
const paragraph = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.'
// 使用 search 搜尋字串是否在段落中,有找到回傳字串的起始位置,沒找到回傳 -1
paragraph.search('tExT') // -1
paragraph.search(/tExT/i) // 28
// 使用 match 找出第一個比對成功的詳細資訊,加上 g flag 則會列出所有比對成功的字串
paragraph.match(/ing/) // ["ing", index: 45, ...]
paragraph.match(/ing/g) // ["ing", "ing"]
const Word = 'apple';
Word.match(/^Ap/); //null
Word.match(/^Ap/i); //["ap", index: 0, input: "apple",..]
Word.match(/^pl/); //null
const Word = 'apple';
Word.match(/Ap$/); //null
Word.match(/LE$/i); //["le", index: 3, input: "apple"..]
Word.match(/le$/); //["le", index: 3, input: "apple"..]
const regex = /color|colour/
regex.exec('color') // ["color", index: 0, ...]
regex.exec('colour') // ["colour", index: 0, ...]
const regex = /\$100/; //$不再表示結尾
regex.test('$100') // true
集合代表著 這一個字元 可以是 [ ] 內的其中一種。
// 只要是英文大寫字母,就比對成功
const regex = /[ABCDEFGHIJKLMNOPQRSTUVWXYZ]/
'K'.match(regex) // ["K", index: 0, ...]
'δ'.match(regex) // null
// 可以使用 '-' 來簡化集合,'A-Z' 表示英文字母 A ~ Z 都符合
const regex = /[A-Z]/
// 若要比對的是英文或數字,可以這樣表示
const regex = /[A-Za-z0-9]/
一些常用的集合有對應的特殊字元。
const regex = /./ // 比對換行符號外的任意一個字元
const regex = /\d/ // 比對一個數字,相等於 /[0-9]/
const regex = /\w/ // 比對一個英文、數字或底線,相等於 /[A-Za-z0-9_]/
const regex = /\s/ // 比對一個的空格 (ex: space, tab, 換行, ...)
使用排除法 [^ ] 來比對這個集合 以外 的字元
const regex = /[^\w]/
regex.test('a') // false
regex.test('!') // true
使用集合一次也只能比對一個文字,若想比對連續的相同規則時,可以使用量詞 { } 來修飾。
// 不使用量詞時,要比對 5 個連續的數字就必須寫 5 次
const regex = /\d\d\d\d\d/
regex.test('12345') // true
// 使用 {5} 表示連續出現 5 次
const regex = /\d{5}/
regex.exec('abcde12345') // ["12345", index: 5, ...]
regex.exec('a1b2c3d4e5') // null
// 使用 {2,} 表示連續出現 2 次以上
const regex = /\w\+{2,}/
regex.exec('a+') // null
regex.exec('a++') // ["a++", index: 0, ...]
// 使用 {2, 5} 表示連續出現 2 ~ 5 次
const regex = /^\w{2,5}!/
regex.exec('Hi!') // ["Hi!", index: 0, ...]
regex.exec('Helloooo!') // null
量詞也有特殊字元可以替代。
// 使用 ? 表示出現 0 或 1 次,等同於 {0,1}
const regex = /\w?/
// 使用 + 表示出現 1 次或以上,等同於 {1,}
const regex = /\w+/
// 使用 * 表示出現 0 次或以上,等同於 {0,}
const regex = /\w*/
使用上,+、?、、{2, 5} 都是屬於 Greedy 量詞,意思是會以連續出現次數 越多 為優先,相反的,在量詞後面加上一個問號 +?、??、?、{2, 5}? 就變成 Lazy 量詞,意思是以連續出現次數 越少 為優先。
// '+' 出現的次數越多優先
const regex = /a\+{2,}/
regex.exec('a+++++') // ["a+++++", index: 0, ...]
// '+' 出現的次數越少優先
const regex = /a\+{2,}?/
regex.exec('a+++++') // ["a++", index: 0, ...]
在字串或段落中搜尋的一個方法,是用來描述字串符合某個語法規則的模型,可以用來做文字的搜尋、比對、萃取、替代、轉換等等。
可以使用兩個 / /
或是new RegExp()
來建立一個RegExp物件。
/
夾住條件const rule = /[a-z]/;
// [] character set; 像是一個搜尋範圍。
//matches a character in the range "a" to "z"
const regex = /some text/;
//使用literal斜線,這種方式會在script載入時就被編譯,效能較好
新增RegExp物件
var rules = new RegExp('[0-9]')
//matches a character in the range "0" to "9"
const regex = new RegExp('some text')
//使用 new 建構一個 RegExp 物件,適合用在需要動態產生 pattern 的場合。
加上flag
const regex = /some text/i ;
// i : 不區分大小寫
const regex = new RegExp('some text', 'g');
// g :比對字串所有位置
可以使用RegExp 物件中的tet
exec
,在String物件
中的search
、match
、replace
、split
等方法中,也有支援正規表達式寫法。
var str = 'happy';
// 驗證字串為 a~z 的字母
const rules = /[a-z]/;
console.log(rules.test(str));
// true
console.log(/\d/.test(str));
// false
// \d 的意思是: match any digit character (0-9)
const regex = /hello world/i
regex.exec('Hello World !!') // ["Hello World", index: 0, input: "Hello World !!", groups: undefined]
regex.exec('Hello Regex !!') // null ; 比對失敗時回傳 null
3.search()、match()
const paragraph = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.'
// 使用 search 搜尋字串是否在段落中,有找到回傳字串的起始位置,沒找到回傳 -1
paragraph.search('tExT') // -1
paragraph.search(/tExT/i) // 28
// 使用 match 找出第一個比對成功的詳細資訊,加上 g flag 則會列出所有比對成功的字串
paragraph.match(/ing/) // ["ing", index: 45, ...]
paragraph.match(/ing/g) // ["ing", "ing"]
const Word = 'apple';
Word.match(/^Ap/); //null
Word.match(/^Ap/i); //["ap", index: 0, input: "apple",..]
Word.match(/^pl/); //null
const Word = 'apple';
Word.match(/Ap$/); //null
Word.match(/LE$/i); //["le", index: 3, input: "apple"..]
Word.match(/le$/); //["le", index: 3, input: "apple"..]
const regex = /color|colour/
regex.exec('color') // ["color", index: 0, ...]
regex.exec('colour') // ["colour", index: 0, ...]
const regex = /\$100/; //$不再表示結尾
regex.test('$100') // true
集合代表著 這一個字元 可以是 [ ] 內的其中一種。
// 只要是英文大寫字母,就比對成功
const regex = /[ABCDEFGHIJKLMNOPQRSTUVWXYZ]/
'K'.match(regex) // ["K", index: 0, ...]
'δ'.match(regex) // null
// 可以使用 '-' 來簡化集合,'A-Z' 表示英文字母 A ~ Z 都符合
const regex = /[A-Z]/
// 若要比對的是英文或數字,可以這樣表示
const regex = /[A-Za-z0-9]/
一些常用的集合有對應的特殊字元。
const regex = /./ // 比對換行符號外的任意一個字元
const regex = /\d/ // 比對一個數字,相等於 /[0-9]/
const regex = /\w/ // 比對一個英文、數字或底線,相等於 /[A-Za-z0-9_]/
const regex = /\s/ // 比對一個的空格 (ex: space, tab, 換行, ...)
使用排除法 [^ ] 來比對這個集合 以外 的字元
const regex = /[^\w]/
regex.test('a') // false
regex.test('!') // true
使用集合一次也只能比對一個文字,若想比對連續的相同規則時,可以使用量詞 { } 來修飾。
// 不使用量詞時,要比對 5 個連續的數字就必須寫 5 次
const regex = /\d\d\d\d\d/
regex.test('12345') // true
// 使用 {5} 表示連續出現 5 次
const regex = /\d{5}/
regex.exec('abcde12345') // ["12345", index: 5, ...]
regex.exec('a1b2c3d4e5') // null
// 使用 {2,} 表示連續出現 2 次以上
const regex = /\w\+{2,}/
regex.exec('a+') // null
regex.exec('a++') // ["a++", index: 0, ...]
// 使用 {2, 5} 表示連續出現 2 ~ 5 次
const regex = /^\w{2,5}!/
regex.exec('Hi!') // ["Hi!", index: 0, ...]
regex.exec('Helloooo!') // null
量詞也有特殊字元可以替代。
// 使用 ? 表示出現 0 或 1 次,等同於 {0,1}
const regex = /\w?/
// 使用 + 表示出現 1 次或以上,等同於 {1,}
const regex = /\w+/
// 使用 * 表示出現 0 次或以上,等同於 {0,}
const regex = /\w*/
使用上,+、?、、{2, 5} 都是屬於 Greedy 量詞,意思是會以連續出現次數 越多 為優先,相反的,在量詞後面加上一個問號 +?、??、?、{2, 5}? 就變成 Lazy 量詞,意思是以連續出現次數 越少 為優先。
// '+' 出現的次數越多優先
const regex = /a\+{2,}/
regex.exec('a+++++') // ["a+++++", index: 0, ...]
// '+' 出現的次數越少優先
const regex = /a\+{2,}?/
regex.exec('a+++++') // ["a++", index: 0, ...]