AJAX – 網路請求
什麼是 AJAX?它如何改善網頁使用體驗?
AJAX 是非同步 JavaScript 與 XML 技術。
什麼是網路請求?
- 透過 Chrome 執行 Enter URL 動作,我傳送了一個網路請求,取得:get URL 網頁上的資訊
- 其他軟體也可以發出網路請求
- 透過 JS 發出網路請求
從網頁架構瞭解網頁請求 – 上集
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js-compulsory</title>
</head>
<body>
<h1>js-compulsory</h1>
<ul class="list"></ul>
<img src="https://images.unsplash.com/photo-1630042111810-af23e1de9176?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=736&q=80" alt="Photo by Lissete Laverde">
<script src="js/all.js"></script>
</body>
</html>
// all.js
console.log('1234');
- Chrome Network – 開發人員工具
從網頁架構瞭解網頁請求 – 下集
網頁請求先後分別是 index.html 的 HTML 結構、 img src、all.js,共發了3次網頁請求。
- Chrome get 取得請求 → 本地端伺服器
- Chrome ← 本地端伺服器
網頁請求狀態碼
-
HTTP 狀態碼 – MDN文件
- 資訊回應 (Informational responses, 100 -199)
- 成功回應 (Successful responses, 200 – 299)
- 重定向 (Redirects , 300 – 399)
- 用戶端錯誤 (Client errors, 400 – 499)
- 伺服器端錯誤 (Server errors, 500 – 599)
狀態碼(Status)有哪些
- 404 Not Found 伺服器找不到請求的資源
- 200 OK 請求成功
- 304 Not Modified
- 500 Internal Server Error 伺服器端發生未知或無法處理的錯誤
清除快取並強制重新載入 (開發人員工具要打開)
- 重新整理圖示長按左鍵後選取清除快取並強制重新載入
- 重新整理圖示右鍵後選取清除快取並強制重新載入
request、response 講解
- Google Network → Name – index.html → Headers → Request Headers
request(請求):傳送給伺服器要什麼資料。
response(回傳):回傳給瀏覽器資料,response header、response data
用 Node.js 開啟伺服器,更加瞭解 request、response 的差異
// app.js
const http = require('http');
http.createServer(function(request, response) {
console.log(request);
if(request.url == '/') {
console.log('接收到網頁請求');
response.writeHead(200, {'Content-Type': 'text/HTML'});
response.write('<h1>index</h1>');
response.end();
}
}).listen(process.env.PORT || 3000);
console.log('Server已開啟port: 3000.');
AJAX – 網路請求小節測驗
AJAX – axios 套件教學
各種發出網路請求的 JS 寫法種類介紹
AJAX 透過 JS 發出網路請求。
JavaScript 原生寫法
// XMLHttpRequest 範例
function reqListener () {
console.log(this.responseText);
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();
// Fetch 範例
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
});
套件,需額外載入 JS
- axios – 連結、Features
axios 環境安裝
套件cdn 程式碼與自己撰寫載入的程式碼載入順序有差
套件程式碼載入放在自己撰寫的程式碼之前。
檢查套件載入有沒有成功
- 使用 Google Network 查看 Status
- 使用 console.log(axios);
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js-compulsory</title>
</head>
<body>
<h1>js-compulsory</h1>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/all.js"></script>
</body>
</html>
// all.js
// 測試套件有沒有載入成功
console.log(axios);
axios – 嘗試串接外部資料
// 範例程式碼
axios.get('https://hexschool.github.io/ajaxHomework/data.json')
.then(function (response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js-compulsory</title>
</head>
<body>
<h1>js-compulsory</h1>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/all.js"></script>
</body>
</html>
// all.js
// 測試套件有沒有載入成功
// console.log(axios);
// https://hexschool.github.io/ajaxHomework/data.json
axios.get('https://hexschool.github.io/ajaxHomework/data.json')
.then(function (response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
axios – response 參數詳細講解
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js-compulsory</title>
</head>
<body>
<h1>js-compulsory</h1>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/all.js"></script>
</body>
</html>
// all.js
// 測試套件有沒有載入成功
// console.log(axios);
// https://hexschool.github.io/ajaxHomework/data.json
axios.get('https://hexschool.github.io/ajaxHomework/data.json')
.then(function (response) {
console.log(response);
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
axios – 將外部資料寫入到網頁上
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js-compulsory</title>
</head>
<body>
<h1>js-compulsory</h1>
<h2 class="title"></h2>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/all.js"></script>
</body>
</html>
// all.js
// 測試套件有沒有載入成功
// console.log(axios);
// https://hexschool.github.io/ajaxHomework/data.json
axios.get('https://hexschool.github.io/ajaxHomework/data.json')
.then(function (response) {
let ary = response.data;
console.log(ary[0].name);
const title = document.querySelector('.title');
// title.innerHTML = '王小明';
// title.innerHTML = ary[0].name;
title.textContent = ary[0].name;
});
我的提問,外部資料圖片抓取問題
// index.html
<ul class="list"></ul>
// style.css
img {
width: 320px;
height: 240px;
}
// script.js
// 外部資料
// https://ptx.transportdata.tw/MOTC/v2/Tourism/ScenicSpot?%24top=40&%24format=JSON
// 資料模擬
let data = [
{
name: "green mountain across body of water",
pictureUrl:
"https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=870&q=80"
},
{
name: "mountains reflected on water"
// 沒有 pictureUrl
},
{
name: "brown field near tree during daytime",
pictureUrl: "" // pictureUrl 為空值
},
{
name: "花瓶岩",
pictureUrl: "http://210.69.151.212/ptngis/files/photos/l/marks/C1-11501.jpg" // pictureUrl 圖片網址錯誤
}
];
const list = document.querySelector(".list");
// 初始化狀態 - 渲染資料
function dataRender() {
// 初始化一個空字串
let str = "";
data.forEach(function (item, index) {
// let content = `<li>
// <h2>${item.name}</h2>
// <img src="${item.pictureUrl}" alt="圖片">
// </li>`;
// str+=content;
let content = `<li>
<h2>${item.name}</h2>
<img src="${item.pictureUrl}" alt="${item.name}" >
</li>`;
str += content;
// }
});
list.innerHTML = str;
}
dataRender();
// 綁定監聽事件
// 最後一項錯誤的網址,瀏覽器會先去找看看有沒有,如果沒有才會觸發。所以可以看到最後一張比較慢才顯示
const images = document.querySelectorAll("img");
images.forEach((item) => {
item.addEventListener("error", (e) => {
// console.log(e.target);
e.target.setAttribute(
"src",
"https://images.unsplash.com/photo-1561657819-51c0511e35ab?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=871&q=80"
);
});
});
// 第一個資料正常:圖片正常
// 第二個沒有資料:使用取代圖片
// 第三個是空值
// 第四個是錯誤的圖片網址
// 關於陣列圖片抓取的問題
// 關於沒有資料、空值、錯誤的圖片網址,該怎麼處理、避免圖片區域是空白情形?
axios – 非同步觀念
// all.js
// 測試套件有沒有載入成功
// console.log(axios);
// https://hexschool.github.io/ajaxHomework/data.json
let ary = [];
axios.get('https://hexschool.github.io/ajaxHomework/data.json')
.then(function (response) {
console.log('資料有回傳了');
ary = response.data;
console.log(ary);
});
console.log(ary);
// Google Console
[] all.js:15
資料有回傳了 all.js:10
[{...}] all.js:12
透過函式設計處理非同步
資料回傳後,再執行函式。
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js-compulsory</title>
</head>
<body>
<h1>js-compulsory</h1>
<h2 class="title"></h2>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/all.js"></script>
</body>
</html>
// all.js
// 測試套件有沒有載入成功
// console.log(axios);
// https://hexschool.github.io/ajaxHomework/data.json
let ary = [];
axios.get('https://hexschool.github.io/ajaxHomework/data.json')
.then(function(response) {
console.log('資料有回傳了'); // 1
ary = response.data;
renderData();
});
// 渲染資料
function renderData() {
console.log(ary); // 2
const title = document.querySelector('.title');
// title.textContent = '測試';
title.textContent = ary[0].name;
}
console.log(ary); // 3
// 執行的先後順序 3 → 1 → 2
AJAX – axios 套件教學小節測驗
第五週總複習
網路請求補充知識 (還未觀看)
從 Wireshark、Fiddler 深入了解網路請求
計算機網路總結
AJAX POST API 講解
網路請求種類介紹
post 網路請求文件介紹
-
本小節練習用 API 網址
- 註冊
- 登入
六角學院 AJAX 練習
注意,此範例僅供練習,並不會儲存用戶資料至資料庫(僅緩存)。
註冊
新增一個帳號。
- Method: POST
- URL: https://hexschool-tutorial.herokuapp.com/api/signup
- Data:
{
email: 'lovef2e@hexschool.com',
password: '12345678'
}
- Success Response:
{
"success": true,
"result": {},
"message": "帳號註冊成功"
}
- Error Response:
{
"success": false,
"result": {},
"message": "此帳號已被使用"
}
登入
登入一個已存在的帳號。
- Method: POST
- URL: https://hexschool-tutorial.herokuapp.com/api/signin
- Data:
{
email: 'lovef2e@hexschool.com',
password: '12345678'
}
- Success Response:
{
"success": true,
"result" {},
"message": "登入成功"
}
- Error Response:
{
"success": false,
"result": {},
"message": "此帳號不存在或帳號密碼錯誤"
}
四種常見的 POST 請求 content-type 介紹
請求資料格式 request header Content-Type
- application/x-www-form-urlencoded
- application/json
- multipart/form-data
- text/plain (記事本格式最少使用)
axios 預設屬於第2種請求資料格式,支援其他格式、可以自己設定。
multipart/form-data,傳送檔案格式的時候,檔案:圖片、pdf、word、mp4。
透過 axios 實作註冊 post 網路請求
// axios post 範例
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/all.js"></script>
</body>
</html>
// all.js
// URL
// https://hexschool-tutorial.herokuapp.com/api/signup
let obj = {
email: 'hexschool2021@hexschool.com',
password: '12345678'
}
axios.post('https://hexschool-tutorial.herokuapp.com/api/signup', obj)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// Google Console
{data: {…}, status: 200, statusText: 'OK', headers: {…}, config: {…}, …}
XHR finished loading: POST "https://hexschool-tutorial.herokuapp.com/api/signup"
>
從 chrome 觀察 post 請求
- Chrome Network
- Name
- Headers
- Payload
- Preview
- Response
// all.js - 1
// URL
// https://hexschool-tutorial.herokuapp.com/api/signup
let obj = {
email: 'hexschool2021@hexschool.com',
password: '12345678'
}
axios.post('https://hexschool-tutorial.herokuapp.com/api/signup', obj)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// index.html - 2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/all.js"></script>
</body>
</html>
// all.js - 2
// URL
// https://hexschool-tutorial.herokuapp.com/api/signup
function callSignUp(){
let obj = {
email: 'hexschool2021@hexschool.com',
password: '12345678'
}
axios.post('https://hexschool-tutorial.herokuapp.com/api/signup', obj)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
// Google Console - 2
> callSignUp();
< undefined
{data: {…}, status: 200, statusText: 'OK', headers: {…}, config: {…}, …}
XHR finished loading: POST "https://hexschool-tutorial.herokuapp.com/api/signup"
>
實作 axios DOM 表單註冊流程
// 範例程式碼 index.html
帳號:
<input type="text" class="account">
<br>
密碼:
<input type="text" class="password">
<br>
<input type="button" value="送出" class="send">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
// 範例程式碼 - script.js
const account = document.querySelector('.account');
const password = document.querySelector('.password');
const send = document.querySelector('.send');
send.addEventListener('click',function(e){
callSingUp()
})
function callSingUp(){
if (account.value == "" || password.value==""){
alert("請填寫正確資訊");
return;
}
let obj = {};
obj.email = account.value;
obj.password = password.value;
axios.post('https://hexschool-tutorial.herokuapp.com/api/signup', obj)
.then(function (response) {
if (response.data.message=="帳號註冊成功"){
alert("恭喜帳號註冊成功");
}else{
alert("帳號註冊失敗,有可能有人用你的email註冊!");
}
account.value = "";
password.value="";
})
.catch(function (error) {
console.log(error);
});
}
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<form>
<h2>註冊帳號</h2>
<label for="account">帳號:</label>
<input id="account" type="text" class="account" placeholder="請輸入帳號">
<br><br>
<label for="password">密碼:</label>
<input id="password" type="password" class="password" placeholder="請輸入密碼">
<br><br>
<input type="button" value="送出" class="send">
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/all.js"></script>
</body>
</html>
// style.css
/* css reset start */
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* css reset end */
/* css reset settings start */
*,*::before,*::after {
box-sizing: border-box;
}
img {
max-width: 100%;
height: auto;
}
/* css reset settings end */
/* css styles start */
*,*:before,*:after {
box-sizing: border-box;
}
.container {
max-width: 940px;
margin: 12% auto;
}
form {
max-width: 375px;
width: 100%;
margin: 0 auto;
background: #eee;
}
form h2 {
font-size: 28px;
text-align: center;
padding-top: 16px;
padding-bottom: 16px;
}
form label {
width: 100%;
display: block;
margin-bottom: 16px;
padding-left: 5%;
font-size: 20px;
}
.account, .password {
width: 90%;
padding-top: 8px;
padding-bottom: 8px;
margin-left: 5%;
margin-right: 5%;
font-size: 20px;
}
.send {
margin-left: 5%;
margin-bottom: 16px;
padding: 8px 16px;
background: #353535;
color: #fff;
border-radius: 4px;
border: 0;
font-size: 20px;
}
.send:hover {
background: #c9184a;
}
/* css styles end */
// all.js
// URL
// https://hexschool-tutorial.herokuapp.com/api/signup
const account = document.querySelector('.account');
const password = document.querySelector('.password');
const send = document.querySelector('.send');
// console.log(account,password,send);
send.addEventListener('click',function(e){
// console.log('是否被點擊');
callSignUp();
});
function callSignUp(){
// email: 'hexschool2021@hexschool.com',
// password: '12345678'
if(account.value == "" || password.value == ""){
alert("請填寫正確資訊");
return;
}
let obj = {};
obj.email = account.value;
obj.password = password.value;
console.log(obj);
axios.post('https://hexschool-tutorial.herokuapp.com/api/signup', obj)
.then(function (response) {
// console.log(response);
// alert(response.data.message);
if(response.data.message == "帳號註冊成功"){
alert("恭喜帳號註冊成功");
}
else {
alert("此帳號註冊失敗,有可能有人用你的email註冊!");
}
account.value = "";
password.value = "";
})
.catch(function (error) {
console.log(error);
});
}
AJAX POST 小節作業
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- 註冊帳號 -->
<div class="container">
<form>
<h2>註冊帳號</h2>
<label for="account-signup">帳號:</label>
<input id="account-signup" type="text" class="account account-signup" placeholder="請輸入帳號">
<br><br>
<label for="password-signup">密碼:</label>
<input id="password-signup" type="password" class="password password-signup" placeholder="請輸入密碼">
<br><br>
<input type="button" value="送出" class="send send-signup">
</form>
</div>
<!-- 登入帳號 -->
<div class="container">
<form>
<h2>登入帳號</h2>
<label for="account-signin">帳號:</label>
<input id="account-signin" type="text" class="account account-signin" placeholder="請輸入帳號">
<br><br>
<label for="password-signin">密碼:</label>
<input id="password-signin" type="password" class="password password-signin" placeholder="請輸入密碼">
<br><br>
<input type="button" value="送出" class="send send-signin">
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/all.js"></script>
</body>
</html>
// style.css
/* css reset start */
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* css reset end */
/* css reset settings start */
*,*::before,*::after {
box-sizing: border-box;
}
img {
max-width: 100%;
height: auto;
}
/* css reset settings end */
/* css styles start */
.container {
max-width: 940px;
margin: 12% auto;
}
form {
max-width: 375px;
width: 100%;
margin: 0 auto;
background: #eee;
}
form h2 {
font-size: 28px;
text-align: center;
padding-top: 16px;
padding-bottom: 16px;
}
form label {
width: 100%;
display: block;
margin-bottom: 16px;
padding-left: 5%;
font-size: 20px;
}
.account, .password {
width: 90%;
padding-top: 8px;
padding-bottom: 8px;
margin-left: 5%;
margin-right: 5%;
font-size: 20px;
}
.send {
margin-left: 5%;
margin-bottom: 16px;
padding: 8px 16px;
background: #353535;
color: #fff;
border-radius: 4px;
border: 0;
font-size: 20px;
}
.send:hover {
background: #c9184a;
}
/* css styles end */
// all.js
// URL
// https://hexschool-tutorial.herokuapp.com/api/signup
const accountSignup = document.querySelector('.account-signup');
const passwordSignup = document.querySelector('.password-signup');
const sendSignup = document.querySelector('.send-signup');
// console.log(accountSignup,passwordSignup,sendSignup);
sendSignup.addEventListener('click',function(e){
// console.log('是否被點擊');
callSignUp();
});
function callSignUp(){
// email: 'hexschool2021@hexschool.com',
// password: '12345678'
if(accountSignup.value == "" || passwordSignup.value == ""){
alert("請填寫正確資訊");
return;
}
let objSignup = {};
objSignup.email = accountSignup.value;
objSignup.password = passwordSignup.value;
// console.log(objSignup);
axios.post('https://hexschool-tutorial.herokuapp.com/api/signup', objSignup)
.then(function (response) {
// console.log(response);
// alert(response.data.message);
if(response.data.message == "帳號註冊成功"){
alert("恭喜帳號註冊成功");
}
else {
alert("此帳號註冊失敗,有可能有人用你的email註冊!");
}
accountSignup.value = "";
passwordSignup.value = "";
})
.catch(function (error) {
console.log(error);
});
}
// URL
// https://hexschool-tutorial.herokuapp.com/api/signin
const accountSignin = document.querySelector('.account-signin');
const passwordSignin = document.querySelector('.password-signin');
const sendSignin = document.querySelector('.send-signin');
// console.log(accountSignin,passwordSignin,sendSignin);
sendSignin.addEventListener('click',function(e){
// console.log('是否被點擊');
callSignin();
});
function callSignin(){
if(accountSignin.value == "" || passwordSignin.value == ""){
alert("請填寫正確資訊");
return;
}
let objSignin = {};
objSignin.email = accountSignin.value;
objSignin.password = passwordSignin.value;
// console.log(objSignin);
axios.post('https://hexschool-tutorial.herokuapp.com/api/signin', objSignin)
.then(function (response) {
// console.log(response);
// alert(response.data.message);
if(response.data.message == '登入成功'){
alert("帳號登入成功");
}
else {
alert('此帳號登入失敗,請重新登入帳號');
}
accountSignin.value = "";
passwordSignin.value = "";
})
.catch(function (error) {
console.log(error);
})
}
AJAX POST API 講解小節測驗
todolist 待辦事項
todolist 經典題目介紹
取值複習 getAttribute
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<h2 class="title">標題內容</h2>
<script src="all.js"></script>
</body>
</html>
// all.js
const title = document.querySelector(".title");
// console.log(title);
title.addEventListener("click",function(e){
// console.log(e.target);
console.log(e.target.textContent);
console.log(e.target.getAttribute("class"));
});
範圍內容取值
方法一
// index.html - 1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<h2 class="title">
<span>標題內容</span>
<input type="button" value="檢視">
</h2>
<script src="all.js"></script>
</body>
</html>
// all.js - 1
const title = document.querySelector(".title");
// console.log(title);
title.addEventListener("click",function(e){
// console.log(e.target);
// console.log(e.target.textContent);
// console.log(e.target.getAttribute("class"));
// console.log(e.target.nodeName);
if (e.target.nodeName!=="INPUT") {
return;
}
// console.log('你有正確點到 input');
console.log(e.target.getAttribute("value"));
});
方法二
// index.html - 2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<h2 class="title">
<span>標題內容</span>
<input type="button" class="view" value="檢視">
</h2>
<script src="all.js"></script>
</body>
</html>
// all.js - 2
const title = document.querySelector(".title");
title.addEventListener("click",function(e){
if (e.target.getAttribute("class")=="view") {
console.log(e.target.getAttribute("value"));
}
});
監聽大範圍內容取值
// index.html - 1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<div class="box">
<h2 class="title">
<span>標題1</span>
<input type="button" class="view" value="檢視1">
</h2>
<h2 class="title">
<span>標題2</span>
<input type="button" class="view" value="檢視2">
</h2>
<h2 class="title">
<span>標題3</span>
<input type="button" class="view" value="檢視3">
</h2>
</div>
<script src="all.js"></script>
</body>
</html>
方法一 – class 比較直覺、不會犯錯
// all.js - 1
const list = document.querySelector('.box');
// console.log(list);
list.addEventListener('click',function(e){
// console.log(e.target.nodeName);
if (e.target.getAttribute('class') == 'view'){
console.log(e.target.getAttribute('value'));
}
});
方法二
// all.js - 2
const list = document.querySelector('.box');
// console.log(list);
list.addEventListener('click',function(e){
// console.log(e.target.nodeName);
if (e.target.nodeName == "INPUT"){
console.log(e.target.getAttribute('value'));
}
});
方法三
// all.js - 3
const list = document.querySelector('.box');
// console.log(list);
list.addEventListener('click',function(e){
// console.log(e.target.nodeName);
if (e.target.nodeName !== "INPUT"){
return;
}
console.log(e.target.getAttribute('value'));
});
data- 屬性妙用
- data- 屬性取值,data-自訂名稱=”值”
- 例如:data-num=”3″、data-content=”標題內容”
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<h2 class="title" data-content="標題內容" data-num="3">標題內容</h2>
<script src="all.js"></script>
</body>
</html>
// all.js
const title = document.querySelector('.title');
// console.log(title);
console.log(title.getAttribute("data-content"));
console.log(title.getAttribute("data-num"));
let data = title.getAttribute("data-content");
console.log(data);
HTML 結構設計
畫面實作
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<input type="text" class="txt" placeholder="請輸入待辦事項">
<input type="button" class="save" value="儲存待辦">
<ul class="list">
<li>待辦事項一 <input class="delete" type="button" value="刪除待辦"></li>
<li>待辦事項二 <input class="delete" type="button" value="刪除待辦"></li>
</ul>
<script src="all.js"></script>
</body>
</html>
資料初始化渲染
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<input type="text" class="txt" placeholder="請輸入待辦事項">
<input type="button" class="save" value="儲存待辦">
<ul class="list">
</ul>
<script src="all.js"></script>
</body>
</html>
// all.js - 1
let data = [
{
"content": "待辦事項一"
},
{
"content": "今天記得刷牙"
}
]
let str = '';
data.forEach(function(item){
str+=`
<li>${item.content} <input class="delete" type="button" value="刪除待辦"></li>
`
});
// console.log(str);
const list = document.querySelector(".list");
list.innerHTML = str;
// all.js - 2
let data = [
{
"content": "待辦事項一"
},
{
"content": "今天記得刷牙"
}
]
function renderData(){
let str = '';
data.forEach(function(item){
str+=`
<li>${item.content} <input class="delete" type="button" value="刪除待辦"></li>
`
});
// console.log(str);
const list = document.querySelector(".list");
list.innerHTML = str;
}
renderData();
待辦新增功能
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<input type="text" class="txt" placeholder="請輸入待辦事項">
<input type="button" class="save" value="儲存待辦">
<ul class="list">
</ul>
<script src="all.js"></script>
</body>
</html>
// all.js
const txt = document.querySelector('.txt');
const save = document.querySelector('.save');
// console.log(txt);
// console.log(save);
let data = [];
function renderData(){
let str = '';
data.forEach(function(item){
str+=`
<li>${item.content} <input class="delete" type="button" value="刪除待辦"></li>
`
});
// console.log(str);
const list = document.querySelector(".list");
list.innerHTML = str;
}
save.addEventListener('click',function(e){
// console.log('你點擊到了');
if(txt.value==""){
alert("請輸入內容");
return;
}
// {
// "content": "待辦事項一"
// }
let obj = {};
obj.content = txt.value;
// console.log(txt.value);
// console.log(obj);
data.push(obj);
renderData();
});
// renderData();
待辦刪除功能-前置解說
- splice 刪除指定資料
- forEach 會用到 index 索引值
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<input type="text" class="txt" placeholder="請輸入待辦事項">
<input type="button" class="save" value="儲存待辦">
<ul class="list">
</ul>
<script src="all.js"></script>
</body>
</html>
// all.js
const txt = document.querySelector('.txt');
const save = document.querySelector('.save');
// console.log(txt);
// console.log(save);
let data = [];
function renderData(){
let str = '';
data.forEach(function(item,index){
str+=`
<li>${item.content} <input class="delete" type="button" data-num="${index}" value="刪除待辦"></li>
`
});
// console.log(str);
const list = document.querySelector(".list");
list.innerHTML = str;
}
save.addEventListener('click',function(e){
// console.log('你點擊到了');
if(txt.value==""){
alert("請輸入內容");
return;
}
// {
// "content": "待辦事項一"
// }
let obj = {};
obj.content = txt.value;
// console.log(txt.value);
// console.log(obj);
data.push(obj);
renderData();
});
// renderData();
待辦刪除功能
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS必修篇</title>
</head>
<body>
<input type="text" class="txt" placeholder="請輸入待辦事項">
<input type="button" class="save" value="儲存待辦">
<ul class="list">
</ul>
<script src="all.js"></script>
</body>
</html>
// all.js
const txt = document.querySelector('.txt');
const save = document.querySelector('.save');
const list = document.querySelector('.list');
// console.log(txt);
// console.log(save);
// console.log(list);
let data = [];
function renderData(){
let str = '';
data.forEach(function(item,index){
str+=`
<li>${item.content} <input class="delete" type="button" data-num="${index}" value="刪除待辦"></li>
`
});
// console.log(str);
const list = document.querySelector(".list");
list.innerHTML = str;
}
// 新增待辦功能
save.addEventListener('click',function(e){
// console.log('你點擊到了');
if(txt.value==""){
alert("請輸入內容");
return;
}
// {
// "content": "待辦事項一"
// }
let obj = {};
obj.content = txt.value;
// console.log(txt.value);
// console.log(obj);
data.push(obj);
renderData();
});
// 刪除待辦功能
list.addEventListener('click',function(e){
// console.log(e.target.nodeName);
if(e.target.getAttribute('class',"delete")!=="delete"){
alert('你目前不是點擊到按鈕');
return;
}
// alert('你目前有確實點到刪除按鈕');
let num = e.target.getAttribute("data-num");
console.log(num);
data.splice(num,1);
renderData();
});
// renderData();
todolist 待辦事項小節測驗
陣列資料操作
陣列操作 – map
JS array map
- 能將原始陣列運算後,重新組合回傳一個新陣列
- 不會影響到原陣列
// all.js - 1
// 第一個範例
const arr = [1,5,10];
const newArr = arr.map(function(item){
return item*item;
});
console.log(newArr);
console.log(arr);
// 第二個範例
const data = [1,8,13,20];
const newData = data.map(function(item){
return item>10;
});
console.log(data);
console.log(newData);
// Google Console - 1
(3) [1, 25, 100]
(3) [1, 5, 10]
(4) [1, 8, 13, 20]
(4) [false, false, true, true]
>
// all.js -2
// 第三個範例
const data = [1,8,13,20];
const newData = data.map(function(item){
let obj = {};
obj.checkNum = item > 10;
return obj;
});
console.log(data);
console.log(newData);
// {
// checkNum: true;
// }
// Google Console - 2
(4) [1, 8, 13, 20]
(4) [{...},{...},{...},{...}]
>
陣列操作 – map 補充
- map 觀念補充
- map、forEach 差異
// all.js - 1
// map 觀念補充、map,forEach 差異
const arr = [1,5,10];
const newArr = arr.map(function(item){
// return item*item;
});
console.log(newArr);
console.log(arr);
// Google Console - 1
(3) [undefined, undefined, undefined]
(3) [1, 5, 10]
>
// all.js - 2
// map 觀念補充、map,forEach 差異
const arr = [1,5,10];
const newArr = arr.forEach(function(item){
return item*item;
});
console.log(newArr);
console.log(arr);
// Google Console - 2
undefined
(3) [1, 5, 10]
>
forEach 並沒有回傳一個新陣列的特性、也沒辦法用 return 回傳值。forEach 使用時機,裡面資料都跑一次,加總、額外處理、空陣列組出要的資料。
// all.js - 3
// map 觀念補充、map,forEach 差異
const arr = [1,5,10];
let total = 0;
arr.forEach(function(item){
total+=item;
});
console.log(arr);
console.log(total);
// Google Console - 3
(3) [1, 5, 10]
16
>
map 使用情境,新變數要拿原始變數回傳一個陣列的時候,處理陣列、回傳一個組合好的陣列,map 就比較合適。
- 有無 return,forEach 沒有 return、map 有 return
陣列操作 – filter 資料篩選
JS array filter 篩選
- 篩選出符合條件的內容
- 不會影響到原陣列
常用於比價、下拉選擇市區、有誰有及格。
// all.js - 1
// JS array filter 篩選
// 1.篩選出符合條件的內容,組合後回傳新陣列。
// 2.不會影響到原陣列
// 常用於比價、下拉選擇市區
const arr = [1,5,10];
const newArr = arr.filter(function(item){
return item >= 5;
});
console.log(newArr);
// Google Console - 1
(2) [5, 10]
>
// all.js - 2
// JS array filter 篩選
// 1.篩選出符合條件的內容,組合後回傳新陣列。
// 2.不會影響到原陣列
// 常用於比價、下拉選擇市區、有誰有及格
// 分數
const scoreData = [
{
name: "小明",
score: 88
},
{
name: "小英",
score: 62
},
{
name: "小花",
score: 53
}
];
const filterScore = scoreData.filter(function(item){
return item.score >= 60;
});
console.log(filterScore);
// Google Console - 2
(2) [{...},{...}]
>
陣列操作 – find 尋找頭一筆資料
// all.js
// JS array find 尋找頭一筆資料
const arr = [1,2,3,5,10,20,30,40];
const newArr = arr.find(function(item){
console.log(item);
return item >= 5;
});
console.log(newArr);
// 分數
const scoreData = [
{
name: "小明",
score: 88
},
{
name: "小英",
score: 62
},
{
name: "小花",
score: 53
}
];
const filterScore = scoreData.find(function(item){
return item.score >= 60;
});
console.log(filterScore);
// Google Console
1
2
3
5
5
{name: '小明', score: 88}
>
陣列操作 – findindex 尋找資料索引
// all.js
// find 值提取出來
// findIndex 索引 編號
const colors = ['red','blue','black'];
const blueIndex = colors.findIndex(function(item){
return item == "blue";
});
console.log(blueIndex);
// 訂單編號
const orders = [
{
name: '小廖',
orderId: '12384955'
},
{
name: '小華',
orderId: '12384945'
},
{
name: '小美',
orderId: '12314955'
},
];
const huaId = orders.findIndex(function(item){
return item.orderId == "12384945";
});
console.log(huaId);
console.log(`這個訂單編號是${orders[huaId].name}`);
// Google Console
1
1
這個訂單編號是小華
>
陣列資料操作小節測驗
箭頭函式
函式陳述式與函式表達式差異
// all.js - 1
// 箭頭函式
// 函式陳述式
function numA (x){
return x * x;
}
// 函式表達式
const numB = function(x){
return x * x;
}
const numC = (x) => {
return x * x;
}
// Google Console - 1
> numA(3)
< 9
> numB(3)
< 9
> numC(3)
< 9
>
// Google Console - 1
> numB
< f (x){
return x * x;
}
> numB(3)
< 9
>
函式陳述式有提升的特性,函式表達式沒有提升的特性。
// all.js - 2
console.log(numA(3));
// 函式陳述式
function numA (x){
return x * x;
}
console.log(numA(3));
// Google Console - 2
9
9
>
// all.js - 3
console.log(numB(3));
// 函式表達式
const numB = function(x){
return x * x;
}
// console.log(numB(3));
// Google Console - 3
x Uncaught ReferenceError: Cannot access 'numB' before initialization at xxx
>
箭頭函式基本寫法
// all.js
// 函式表達式
const numA = function(x){
return x * x;
}
console.log(numA(3));
// 箭頭函式
const numB = (x) =>{
return x * x * x;
}
console.log(numB(4));
// Google Console
9
64
>
箭頭函式再縮寫
縮寫寫法
- 如果函式搭配到 return
- 如果只有一個參數,可以省略括號
- 沒有參數,還是要有空括號
// all.js
// 箭頭函式
// 縮寫寫法
// 1.如果函式搭配到 return
// 2.如果只有一個參數,可以省略括號
// 3.沒有參數,還是要有空括號
const numA = (x) => {
return `數字相乘 ${x*x}`;
};
console.log(numA(3));
const numB = (x) => `數字相乘 ${x*x}`;
console.log(numB(4));
const numC = x => `數字相乘 ${x*x}`;
console.log(numC(5));
const num = () => `數字相乘 ${9}`;
console.log(num());
// Google Console
數字相乘 9
數字相乘 16
數字相乘 25
數字相乘 9
>
陣列 map 搭配箭頭函式寫法
// all.js - 1
// 陣列操作 map、filter
// const data = [1,8,13,20];
// const newData = data.map(function(item){
// return item + 2;
// });
// console.log(newData);
const data = [1,8,13,20];
const newData = data.map(item => item + 3);
console.log(newData);
// Google Console - 1
(4) [4, 11, 16, 23]
>
單行可以使用箭頭函式再縮寫,多行還是建議需要加上大括號。
箭頭函式小節測驗
第六週總複習
最終關卡 – todolist 待辦事項 (未製作)
JS – 彩蛋課程 – NPM 管理
NPM 介紹
NPM 套件管理工具。
Node.js、NPM 環境安裝
資源連結
Node.js 安裝選擇 LTS(長期支援)版本安裝,比較穩定。
VSCode 終端機教學
版本檢查
- 檢查 Node.js 版本: node -v
- 檢查 NPM 版本: npm -v
開啟資料夾方式
- 直接拖曳資料夾到 VSCode
- 從 VSCode 選單 File → Open Folder
開啟終端機
- 從 VSCode 選單 View → Terminal 開啟終端機面板
- 終端機 – 輸入指令
- node -v
- npm-v
- 都有回報版本號就有安裝成功
npm init – 專案環境初始化
指令
- 專案初始化:npm init
- 專案初始化,並提供預設值:npm init -y
開啟 Terminal 介面
- View → Terminal、或者快捷鍵 Ctrl + `
- 確認是否在專案位置
- 輸入指令 npm init
- 完成設定後會產生 package.json
各種套件,都會同步記錄在 package.json。
npm install – 環境安裝教學
- NPM 官網
- 安裝模組指令:npm install 模組名稱
備註:若您是 Mac 電腦,有時可能會因為權限不夠而出現紅字 err 出錯,此時請嘗試將指令改為:sudo npm install 模組名稱
- bootstrap – npm i bootstrap 或者 npm install bootstrap
- vue – npm i vue 或者 npm install vue
npm 全域安裝 -g
NPM 安裝範圍。
- package.json 檔案裡面的 dependencies 的套件,可以使用 npm install 指令安裝
- node_modules 不會進到 git 版本控制,通常來說只放開發的 Code
- package.json 裡面的 dependencies 可以安裝自己想要安裝的套件版本號
- 全域空間 – npm install jquery -g
- 建議還是先把套案安裝在專案資料夾
- 可能會運用在 jest, mocha, express…等
- Mac 的話會安裝在 /usr/local/lib/node_modules
練習
- 把專案資料夾裡面 node_modules 刪除、然後使用 npm install 安裝回來
- 可以練習使用全域空間 npm install 模組名稱 -g
–save、–save-dev 指令差異
- production (上線)依賴模組:npm install 模組名稱 –save
- development (開發)依賴模組:npm install 模組名稱 –save-dev
Webpack套件
- Webpack 官網
- 安裝套件指令 npm install webpack –save-dev
練習
- npm install bootstrap –save 套件安裝是在 dependencies (上線階段)
- npm install bootstrap –save-dev 套件安裝是在 devDependencies (開發階段)
- npm install bootstrap 套件安裝是在 dependencies (上線階段)
常見指令補充
- 更新套件:npm update 模組名稱
- 移除套件:npm uninstall 模組名稱
舉例
- npm uninstall jquery
- npm uninstall webpack
- npm uninstall vue
- npm uninstall bootstrap
- npm uninstall bootstrap -g
- npm update vue
也可以從 package.json 修改套件的名稱、版本號,再使用 npm install 安裝套件。
webpack 壓縮打包工具管理
為什麼要學 webpack?
前端壓縮打包工具。
舉例
- vue
- react
- 前端應用
- spa (單頁面應用)
webpack 環境建立
- 安裝 Webpack 套件指令:npm install webpack webpack-cli –save-dev
環境建立步驟
- 建立一個專案資料夾 – webpack-demo
- 開啟終端機
- 專案環境初始化 – npm init、快速初始化 npm init -y
- webpack 套件指令:npm install webpack webpack-cli –save-dev,這裡會安裝在開發環境
- 在 package.json 的 devDependencies 裡面會產生webpack、webpack-cli 名稱、版本號就代表 Webpack 環境建立好了
Webpack Guides
進入點(entry)、輸出點(output) 觀念建立
Download
Webpack Concepts
Webpack 官網圖片講解
- MODULES WITH DEPENDENCIES→bundle your scripts→STATIC ASSETS
- 進入點 – MODULES WITH DEPENDENCIES
- 輸出點 – STATIC ASSETS
操作步驟
- 建立 src 資料夾 – 這個就是進入點、然後在裡面建立 index.js 檔案
- 建立 dist 資料夾 – 這個就是輸出點
- 在 package.json 新增 “build”: “webpack” 名稱、值
- 在終端機輸入指令:npm run build,build 叫做編譯,
在 dist 資料夾就會新增一個 main.js 檔案
// src/index.js
let a = 1;
let b = 2;
console.log("hello");
function hello(a,b){
console.log(a+b);
}
hello(1,2);
// package.json - 新增 "build": "webpack"
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1"
}
}
// dist/main.js
console.log("hello"),console.log(3);
webpack.config.js 環境建立
客製化進入點、輸出點資料夾名稱、以及裡面的檔案。
操作步驟
- 新增 webpack.config.js 檔案
- 客製化進入點路徑、輸出點檔案名稱
- 移除輸出點 main.js 檔案
- 開啟終端機執行指令:npm run build
// webpack.config.js - 範例
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js',
},
};
// webpack.config.js - 客製化
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
};
講解
- path.resolve(__dirname, ‘dist’),可以使用 console.log 查看
NPM script 講解
可以自訂、客製化指令。
操作步驟講解
- 在根目錄上新增 hello.js 檔案
- 透過 package.json 的 “scripts” 的指令
- 在 “scripts” 新增 “hello”: “node hello.js”
- 打開終端機輸入指令:npm run hello
// hello.js
console.log("hello1");
console.log("hello2");
console.log("hello3");
// package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"hello": "node hello.js",
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1"
}
}
開發、上線模式 mode 切換
終端機執行指令npm run build”,出現的訊息 “mode” option has not been set、”mode” option to ‘development’ or ‘production’ 該如何調整、差異在哪裡。
操作步驟
- 在 package.json 把原本 “build”: “webpack” 改寫成開發模式 “dev”: “webpack –mode development”
- 在 package.json 新增一個上線模式 “deploy”: “webpack –mode production”
- 最後的結果會影響到 dist/bundle.js 檔案,他的狀況會有些差異
// package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"hello": "node hello.js",
"dev": "webpack --mode development",
"deploy": "webpack --mode production"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1"
}
}
// dist/bundle.js - 開發模式
/*
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./src/index.js":
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/***/ (() => {
eval("let a = 1;\r\nlet b = 2;\r\nconsole.log(\"hello\");\r\nfunction hello(a,b){\r\n console.log(a+b);\r\n}\r\nhello(1,2);\n\n//# sourceURL=webpack://webpack-demo/./src/index.js?");
/***/ })
/******/ });
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module can't be inlined because the eval devtool is used.
/******/ var __webpack_exports__ = {};
/******/ __webpack_modules__["./src/index.js"]();
/******/
/******/ })()
;
// dits/bundle.js - 上線模式
console.log("hello"),console.log(3);
開發模式產生的 bundle.js 幫助除錯、狀況條列,通常開發的時候都會用開發模式。
上線模式產生的 bundle.js 比較精簡、把程式碼壓縮成一行、做各種優化。
第二種寫法 (比較少寫在這裡)
- 在 webpack.config.js 寫上 mode: ‘production’
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
};
比較少寫在 webpack.config.js 原因是通常會透過 package.json 用下指令的方式去做開發。
export、import 語法教學
- JavaScript 模組化 – JavaScript modules 模塊
- import
- export
- JS 模組化又稱作 ES Module
預設匯出 – 操作步驟
- 新增資料夾 ESModule 放 modules 做練習
- 使用 VSCode 開啟 ESModule 專案
- 新增一個 index.js 檔案
- 新增一個 export1.js 檔案
- 新增一個 index.html 檔案、並載入 <script> index.js、用 ESModule 要加上 type=”module” 這樣子 import export 語法才會生效
// index.js - 1
import data from "./export1.js";
console.log(data);
// export1.js - 1
export default 1;
// 預設匯出
// index.html - 1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="index.js" type="module"></script>
</body>
</html>
// Google Console - 1
1
>
// index.js - 2
import data from "./export1.js";
console.log(data.a);
// export1.js - 2
let a = 1;
let b = 2;
let c = a+b;
export default {
a: 5,
content: b
};
// 預設匯出 default export
// Google Console - 2
5
>
其他常見做法 – 具名匯出
- 新增 export2.js 檔案
- 匯入 index.js 檔案
- 使用 * 匯入、代表全部都載入,通常不建議這樣使用,會不好除錯,使用 * 的時候要搭配 as 自訂名稱
// export2.js - 1
// 具名匯出
export const c = 1;
export function add(x,y){
return x+y;
}
// index.js - 1
import data from "./export1.js";
import {c,add} from "./export2.js";
console.log(data.a);
console.log(c);
console.log(add(2,9));
// Google Console - 1
5
1
11
>
// index.js - 2
import data from "./export1.js";
import {add} from "./export2.js";
console.log(data.a);
// console.log(c);
console.log(add(2,9));
// Google Console - 2
5
11
>
// index.js - 3
import data from "./export1.js";
import * as data2 from "./export2.js";
console.log(data.a);
// console.log(c);
console.log(data2.add(3,88));
// Google Console - 3
5
91
>
匯出做法
- 預設匯出
- 具名匯出
依照專案團隊開發共同討論出共識要使用那種做法。
依老師來說會使用預設匯出的做法。
補充做法(冷門)
// export3.js
function a(){
console.log(88);
}
a();
// index.js
import "./export3.js"
// Google Console
88
>
預設匯出、具名匯出混用
// export2.js
// 具名匯出
export const c = 1;
export function add(x,y){
return x+y;
}
export default 8;
// index.js
import data from "./export1.js";
import total, {c,add} from "./export2.js";
console.log(data.a);
// console.log(c);
console.log(add(3,88));
console.log(total);
// import "./export3.js"
// Google Console
5
91
8
>
webpack import 載入流程
Download
操作步驟
- 新增一個 c.js 在 src 資料夾裡
- 在 src/index.js 匯入 c.js
- 使用終端機執行指令(從 package.json 的指令) npm run dev
- 如果要看編譯模式執行指令 npm run deploy
// src/c.js
export default 333;
// src/index.js
import c from './c.js';
let a = 1;
let b = 2;
console.log("hello");
function hello(a,b){
console.log(a+b);
}
hello(1,2);
console.log(c);
加入 index.html 顯示 bundle.js 結果
Download
操作講解
- 在 dist 資料夾裡新增 index.html
- 載入 <script> bundle.js
- 打開 dist/index.html 看 Google Console 有無資料顯示、正確載入 bundle.js
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>標題</h1>
<script src="./bundle.js"></script>
</body>
</html>
載入 CSS-loader 流程
Download
操作講解
- 安裝 css-loader 指令:npm install –save-dev css-loader
- 新增一個 css 檔案:style.css 放在 src 資料夾裡面
- 環境設定,在 webpack.config.js
- 在 index.js 匯入 import css from “./style.css”;
- 執行 npm run deploy
// src/style.css
h1 {
color: red;
}
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
};
// index.js
import c from './c.js';
import css from "./style.css";
let a = 1;
let b = 2;
console.log("hello");
function hello(a,b){
console.log(a+b);
}
hello(1,2);
console.log(c);
出現錯誤
- 缺少 style-loader 套件安裝,npm install style-loader –save-dev
- 再執行 npm run deploy
- 打開 dist/index.html 看標題是否有變紅色
載入 Sass-loader 流程
Download
Sass 是 SCSS 預處理器
操作步驟
- 安裝 sass-loader、指令:npm install sass-loader sass webpack –save-dev,有安裝的可以不用再安裝、sass 或者 node-sass 這兩個都可以擇一即可,這裡選擇 node-sass 使用
- 進入點 src/index.js 依照文件範例去修改 import “./style.scss”;
- 把 src/style.css 改成 src/style.scss、然後把程式碼貼上、調整
- webpack.config.js 調整成 sass-loader 文件上範例的程式碼
- 執行 npm run deploy
- 打開 dist/index.html 查看標題是否有正確載入變粉紅色
// src/index.js
import c from './c.js';
import css from "./style.scss";
let a = 1;
let b = 2;
console.log("hello");
function hello(a,b){
console.log(a+b);
}
hello(1,2);
console.log(c);
// src/style.scss
$body-color: pink;
h1 {
color: $body-color;
}
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader",
],
},
],
},
};
載入 webpack 測試伺服器
即時更新、監聽。
Download
更新說明
由於 dev server 版本更新(v3 -> v4)因此設定方式的寫法也有些差異
v3 版本寫法
devServer: {
contentBase: path.join(__dirname, "dist"),
}
v4 版本寫法
devServer: {
static: {
directory: path.join(__dirname, 'dist'),
},
}
操作步驟
- 安裝模組指令:npm install webpack-dev-server –save-dev
- 使用上用 CLI,指令是:npx webpack serve,npx 可以不用只需要 webpack serve 這個指令就可以
- NPM Scripts,”scripts” 指令要加上 “serve”: “webpack serve”
- webpack.config.js 也需要做些資料夾設定,從 Webpack devServer 找到 webpack.config.js 範例程式碼複製貼上
- 把 package.json “scripts” 的 “dev” 改成 “dev”: “webpack serve –mode development”
- 執行 npm run dev
- 網址可以輸入 127.0.0.1:9000
// package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"hello": "node hello.js",
"dev": "webpack --mode development",
"deploy": "webpack --mode production",
"serve": "webpack serve"
},
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^6.5.1",
"node-sass": "^7.0.1",
"sass-loader": "^12.4.0",
"style-loader": "^3.3.1",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.7.2"
}
}
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader",
],
},
],
},
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 9000,
},
};
// package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"hello": "node hello.js",
"dev": "webpack serve --mode development",
"deploy": "webpack --mode production",
"serve": "webpack serve"
},
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^6.5.1",
"node-sass": "^7.0.1",
"sass-loader": "^12.4.0",
"style-loader": "^3.3.1",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.7.2"
}
}
出現錯誤
- webpack.config.js 的 devServer ‘public’ 改成 ‘dist’
- 終止批次工作
- 重新執行 npm run dev
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader",
],
},
],
},
devServer: {
static: {
directory: path.join(__dirname, 'dist'),
},
compress: true,
port: 9000,
},
};
自動開啟伺服器網頁
- webpack.config.js 的 devServer 新增 open: true
- 終止批次工作 Ctrl + c
- 重新執行 npm run dev
- 在 style.scss 把 pink 改成 blue 看是否有即時更新
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader",
],
},
],
},
devServer: {
static: {
directory: path.join(__dirname, 'dist'),
},
compress: true,
port: 9000,
open: true
},
};
載入第三方套件 axios – 以 import 為例
Download
操作步驟
- 安裝 axios 套件:npm install axios –save
- 在進入點 src/index.js 寫 import axios from “axios”;,會從 node_modules 找、看有沒有載入的檔案
- 撰寫 axios get
- 執行 npm run dev 看是否有載入成功
// src/index.js
import c from './c.js';
import css from "./style.scss";
import axios from "axios";
axios.get("https://hexschool.github.io/ajaxHomework/data.json")
.then(function(response){
console.log(response);
});
let a = 1;
let b = 2;
console.log("hello");
function hello(a,b){
console.log(a+b);
}
hello(1,2);
console.log(c);