Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
本文主要理理js模塊化相關(guān)知識。
涉及到內(nèi)聯(lián)腳本、外聯(lián)腳本、動態(tài)腳本、阻塞、defer、async、CommonJS、AMD、CMD、UMD、ES Module。順帶探究下Vite。
假設(shè)你是一個前端新手,現(xiàn)在入門,那么我們創(chuàng)建一個html頁面,需要新建一個index.html文件:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<p id="content">hello world</p>
</body>
</html>
如果需要在頁面中執(zhí)行javascript代碼,我們就需要在 HTML 頁面中插入 <script> 標(biāo)簽。
有2種插入方式:
1、放在<head>中
2、放在<body>中
比如,點(diǎn)擊hello world之后,在hello world后面加3個感嘆號的功能,我們在head中加入script標(biāo)簽,并給hello world綁定點(diǎn)擊事件:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<script>
function myFunction() {
document.getElementById('content').innerHTML = 'hello world!!!'
}
</script>
</head>
<body>
<p id="content" onclick="myFunction()">hello world</p>
</body>
</html>
如果加在body中,一般放在body的最后面:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<p id="content" onclick="myFunction()">hello world</p>
<script>
function myFunction() {
document.getElementById('content').innerHTML = 'hello world!!!'
}
</script>
</body>
</html>
簡單的邏輯我們可以用這2種方式寫,這種方式叫做內(nèi)聯(lián)腳本。
當(dāng)邏輯復(fù)雜時,我們可以把上面的script標(biāo)簽中的代碼抽取出來,比如在html的同級目錄創(chuàng)建一個js文件夾,里面新建一個a.js的文件。
a.js中寫上面script標(biāo)簽中的代碼:
function myFunction() {
document.getElementById('content').innerHTML = 'hello world!!!'
}
上面的script標(biāo)簽則可以改成:
<script src="./js/a.js"></script>
上面的2種寫法,瀏覽器在加載html時,遇到script標(biāo)簽,會停止解析html。
內(nèi)聯(lián)腳本會立刻執(zhí)行;外聯(lián)腳本會先下載再立刻執(zhí)行。
等腳本執(zhí)行完畢才會繼續(xù)解析html。
(html解析到哪里,頁面就能顯示到哪里,用戶也能看到哪里)
比如下面的代碼:
<p>...content before script...</p>
<script src="./js/a.js"></script>
<p>...content after script...</p>
解析到第一個p標(biāo)簽,我們能看到...content before script...顯示在了頁面中,然后瀏覽器遇到script標(biāo)簽,會停止解析html,而去下載a.js并執(zhí)行,執(zhí)行完a.js才會繼續(xù)解析html,然后頁面中才會出現(xiàn)...content after script...。
我們可以通過Chrome的Developer Tools分析一下index.html加載的時間線:
這會導(dǎo)致2個問題:
1、腳本無法訪問它下面的dom;
2、如果頁面頂部有個笨重的腳本,在它執(zhí)行完之前,用戶都看不到完整的頁面。
對于問題2,我們可以把腳本放在頁面底部,這樣它可以訪問到上面的dom,且不會阻塞頁面的顯示:
<body>
...all content is above the script...
<script src="./js/a.js"></script>
</body>
但這不是最好的辦法,我們接著往下看。
我們給script標(biāo)簽加defer屬性,就像下面這樣:
<p>...content before script...</p>
<script defer src="./js/a.js"></script>
<p>...content after script...</p>
defer 特性告訴瀏覽器不要等待腳本。于是,瀏覽器將繼續(xù)解析html,腳本會并行下載,然后等 DOM 構(gòu)建完成后,腳本才會執(zhí)行。
這樣script標(biāo)簽不再阻塞html的解析。
這時再看時間線:
需要注意的是,具有 defer 特性的腳本保持其相對順序。
比如:
<script defer src="./js/a.js"></script>
<script defer src="./js/b.js"></script>
上面的2個腳本會并行下載,但是不論哪個先下載完成,都是先執(zhí)行a.js,a.js執(zhí)行完才會執(zhí)行b.js。
這時,如果b.js依賴a.js,這種寫法將很有用。
另外需要注意的是,defer 特性僅適用于外聯(lián)腳本,即如果 script標(biāo)簽沒有 src屬性,則會忽略 defer 特性。
我們可以給script標(biāo)簽加async屬性,就像下面這樣:
<script async src="./js/a.js"></script>
這會告訴瀏覽器,該腳本完全獨(dú)立。
獨(dú)立的意思是,DOM 和其他腳本不會等待它,它也不會等待其它東西。async 腳本就是一個會在加載完成時立即執(zhí)行的完全獨(dú)立的腳本。
這時再看時間線:
可以看到,雖然下載a.js不阻塞html的解析,但是執(zhí)行a.js會阻塞。
還需要注意多個async時的執(zhí)行順序,比如下面這段代碼:
<p>...content before script...</p>
<script async src="./js/a.js"></script>
<script async src="./js/b.js"></script>
<p>...content after script...</p>
兩個p標(biāo)簽的內(nèi)容會立刻顯示出來,a.js和b.js則并行下載,且下載成功后立刻執(zhí)行,所以多個async時的執(zhí)行順序是誰先下載成功誰先執(zhí)行。
一些比較獨(dú)立的腳本,比如性能監(jiān)控,就很適合用這種方式加載。
另外,和defer一樣,async 特性也僅適用于外聯(lián)腳本。
我們可以動態(tài)地創(chuàng)建一個script標(biāo)簽并append到文檔中。
let script = document.createElement('script')
script.src = '/js/a.js'
document.body.append(script)
append后腳本就會立刻開始加載,表現(xiàn)默認(rèn)和加了async屬性一致。
我們可以顯示的設(shè)置script.async = false來改變這個默認(rèn)行為,那么這時表現(xiàn)就和加了defer屬性一致。
上面的這些寫法,當(dāng)script標(biāo)簽變多時,容易導(dǎo)致全局作用域污染,還要維護(hù)書寫順序,要解決這個問題,需要一種將 JavaScript 程序拆分為可按需導(dǎo)入的單獨(dú)模塊的機(jī)制,即js模塊化,我們接著往下看。
很長一段時間 JavaScript 沒有模塊化的概念,直到 Node.js 的誕生,把 JavaScript 帶到服務(wù)端,這時,CommonJS誕生了。
CommonJS定義了三個全局變量:
require,exports,module
require 讀入并執(zhí)行一個 js 文件,然后返回其 exports 對象;
exports 對外暴露模塊的接口,可以是任何類型,指向 module.exports;
module 是當(dāng)前模塊,exports 是 module 上的一個屬性。
Node.js 使用了CommonJS規(guī)范。
比如:
// a.js
let name = 'Lily'
export.name = name
// b.js
let a = require('a.js')
console.log(a.name) // Lily
由于CommonJS不適合瀏覽器端,于是出現(xiàn)了AMD和CMD規(guī)范。
AMD(Asynchronous Module Definition) 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。
基本思想是,通過 define 方法,將代碼定義為模塊。當(dāng)這個模塊被 require 時,開始加載依賴的模塊,當(dāng)所有依賴的模塊加載完成后,開始執(zhí)行回調(diào)函數(shù),返回該模塊導(dǎo)出的值。
使用時,需要先引入require.js:
<script src="require.js"></script>
<script src="a.js"></script>
然后可以這樣寫:
// a.js
define(function() {
let name = 'Lily'
return {
name
}
})
// b.js
define(['a.js'], function(a) {
let name = 'Bob'
console.log(a.name) // Lily
return {
name
}
})
CMD(Common Module Definition) 是 Sea.js 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。
使用時,需要先引入sea.js:
<script src="sea.js"></script>
<script src="a.js"></script>
然后可以這樣寫:
// a.js
define(function(require, exports, module) {
var name = 'Lily'
exports.name = name
})
// b.js
define(function(require, exports, module) {
var name = 'Bob'
var a = require('a.js')
console.log(a.name) // 'Lily'
exports.name = name
})
UMD (Universal Module Definition) 目的是提供一個前后端跨平臺的解決方案(兼容全局變量、AMD、CMD和CommonJS)。
實(shí)現(xiàn)很簡單,判斷不同的環(huán)境,然后以不同的方式導(dǎo)出模塊:
(function (root, factory) {
if (typeof define === 'function' && (define.amd || define.cmd)) {
// AMD、CMD
define([], factory);
} else if (typeof module !== 'undefined' && typeof exports === 'object') {
// Node、CommonJS
module.exports = factory();
} else {
// 瀏覽器全局變量
root.moduleName = factory();
}
}(this, function () {
// 只需要返回一個值作為模塊的export
// 這里我們返回了一個空對象
// 你也可以返回一個函數(shù)
return {};
}));
AMD 和 CMD 是社區(qū)的開發(fā)者們制定的模塊加載方案,并不是語言層面的標(biāo)準(zhǔn)。從 ES6 開始,在語言標(biāo)準(zhǔn)的層面上,實(shí)現(xiàn)了模塊化功能,而且實(shí)現(xiàn)得相當(dāng)簡單,完全可以取代上文的規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案。
ES6 的模塊自動采用嚴(yán)格模式。模塊功能主要由兩個命令構(gòu)成:export和import。
export命令用于規(guī)定模塊的對外接口;
import命令用于輸入其他模塊提供的功能。
比如上面的代碼,我們可以這樣寫:
// a.js
const name = 'Lily'
export {
name
}
// 等價于
export const name = 'Lily'
// b.js
import { name } from 'a.js'
console.log(name) // Lily
// b.js
import * as a from 'a.js'
console.log(a.name) // Lily
此外,還可以用export default默認(rèn)導(dǎo)出的寫法:
// a.js
const name = 'Lily'
export default {
name
}
// b.js
import a from 'a.js'
console.log(a.name) // Lily
如果只想運(yùn)行a.js,可以只import:
// b.js
import 'a.js'
我們可以給script標(biāo)簽加type=module讓瀏覽器以 ES Module 的方式加載腳本:
<script type="module" src="./js/b.js"></script>
這時,script標(biāo)簽會默認(rèn)有defer屬性(也可以設(shè)置成async),支持內(nèi)聯(lián)和外聯(lián)腳本。
這時我們運(yùn)行打開index.html,會發(fā)現(xiàn)瀏覽器報(bào)錯了:
這是因?yàn)?type=module 的 script 標(biāo)簽加強(qiáng)了安全策略,瀏覽器加載不同域的腳本資源時,如果服務(wù)器未返回有效的 Allow-Origin 相關(guān) CORS 頭,會禁止加載改腳本。而這里啟動的index.html是一個本地文件(地址是file://路徑),將會遇到 CORS 錯誤,需要通過一個服務(wù)器來啟動 HTML 文件。
在瀏覽器支持 ES Module 之前,我們用工具實(shí)現(xiàn)JavaScript模塊化的開發(fā),比如webpack、Rollup 和 Parcel 。但是當(dāng)項(xiàng)目越來越大后,本地?zé)岣略絹碓铰?Vite 旨在利用ESM解決上述問題。
Vite使用簡單,可以去官網(wǎng)(https://cn.vitejs.dev/)看看。
老的規(guī)范了解即可,未來是ES Module的,用Vite可以極大的提升開發(fā)時的體驗(yàn),生產(chǎn)環(huán)境用Rollup打包。
Image 對象
Image 對象代表嵌入的圖像。
<img> 標(biāo)簽每出現(xiàn)一次,一個 Image 對象就會被創(chuàng)建。
Image 對象屬性
W3C: W3C 標(biāo)準(zhǔn)。
屬性 | 描述 | W3C |
---|---|---|
align | 設(shè)置或返回與內(nèi)聯(lián)內(nèi)容的對齊方式。 | Yes |
alt | 設(shè)置或返回?zé)o法顯示圖像時的替代文本。 | Yes |
border | 設(shè)置或返回圖像周圍的邊框。 | Yes |
complete | 返回瀏覽器是否已完成對圖像的加載。 | No |
height | 設(shè)置或返回圖像的高度。 | Yes |
hspace | 設(shè)置或返回圖像左側(cè)和右側(cè)的空白。 | Yes |
longDesc | 設(shè)置或返回指向包含圖像描述的文檔的 URL。 | Yes |
lowsrc | 設(shè)置或返回指向圖像的低分辨率版本的 URL。 | No |
name | 設(shè)置或返回圖像的名稱。 | Yes |
src | 設(shè)置或返回圖像的 URL。 | Yes |
useMap | 設(shè)置或返回客戶端圖像映射的 usemap 屬性的值。 | Yes |
vspace | 設(shè)置或返回圖像的頂部和底部的空白。 | Yes |
width | 設(shè)置或返回圖像的寬度。 | Yes |
Image 對象事件
事件 | 描述 | W3C |
---|---|---|
onabort | 當(dāng)用戶放棄圖像的裝載時調(diào)用的事件句柄。 | Yes |
onerror | 在裝載圖像的過程中發(fā)生錯誤時調(diào)用的事件句柄。 | Yes |
onload | 當(dāng)圖像裝載完畢時調(diào)用的事件句柄。 | Yes |
標(biāo)準(zhǔn)屬性和事件
Image 對象同樣支持標(biāo)準(zhǔn)的 屬性 和 事件。
如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!
昨天我們在《使用HTML添加表格3(間距與顏色)——零基礎(chǔ)自學(xué)網(wǎng)頁制作》(目錄在結(jié)尾)中學(xué)習(xí)了設(shè)置單元格以及其中內(nèi)容的空間間距和背景顏色。
其中添加列向單元格背景顏色只需要修改對應(yīng)的<tr>標(biāo)簽中的style屬性,而修改行向標(biāo)簽需要修改不同<tr></tr>標(biāo)簽中的<td>標(biāo)簽的style屬性,這樣操作起來就非常的麻煩,那有沒有簡便的修改行向單元格背景顏色的方法呢?
當(dāng)然有!
開發(fā)團(tuán)隊(duì)給出了<colgroup><col></col></colgroup>這樣的組合來解決這個問題,下面讓我們詳細(xì)學(xué)習(xí)。
<colgroup></colgroup>標(biāo)簽是一個給行向單元格打組的標(biāo)簽,在頁面中不會顯示。
<col></col>標(biāo)簽是來具體設(shè)置行向單元格數(shù)量和顏色的標(biāo)簽。
示例代碼如下:
<colgroup><col span = "1" style="background-color:#ff0000;"></col></colgroup>
這段代碼添加到"第一個頁面.html"當(dāng)中就可以,具體代碼如下:
<!DOCTYPE HTML>
<html>
<head>
<title>第一個網(wǎng)頁</title>
</head>
<body>
<h1>第一個網(wǎng)頁</h1><hr>
<h2>表格元素</h2><hr>
<table border="1" width="100%">
<thead>
<tr>
<td colspan="2">表格的頭部信息</td>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="2">表格的腳部信息</td>
<tr>
</tfoot>
<tbody>
<caption>表格標(biāo)題</caption>
<colgroup>
<col span = "1" style="background-color:#ff0000;"></col>
</colgroup>
<tr>
<th>姓名</th>
<th>年齡</th>
</tr>
<tr>
<td>一列一行</td>
<td>一列二行</td>
</tr>
<tr>
<td>二列一行</td>
<td>二列二行</td>
</tr>
</tbody>
</table>
</body>
</html>
頁面效果如圖:
因?yàn)榈谝涣泻妥詈笠涣兄挥幸恍兴裕捕甲兗t了。
其中span的數(shù)量代表行數(shù)。
如果把span等號后面的數(shù)改成2,因?yàn)楸砀裰挥袃尚校哉麄€表格都紅了。
表格嵌套
我們可以通過向表格中添加表格實(shí)現(xiàn)表格嵌套。表格嵌套可以把一個單元格分成行向或列向分割單元格。
代碼示例如下:我們把"一列一行"分割成列向兩個單元格。
<tr><td><table border = "1" width="100%"><tr><td>1</td><td>2</td></tr></table></td>
使用
<table border = "1" width="100%">
<tr>
<td>1</td>
<td>2</td>
</tr>
</table>
這段代碼替換文字"一列一行"即可。
頁面效果如圖所示:
留個思考題,大家可以思考一下行向分割單元格怎么寫。
今天的內(nèi)容結(jié)束了。
全部示例代碼如下:
<!DOCTYPE HTML>
<html>
<head>
<title>第一個網(wǎng)頁</title>
</head>
<body>
<h1>第一個網(wǎng)頁</h1><hr>
<h2>表格元素</h2><hr>
<table border="1" width="100%">
<thead>
<tr>
<td colspan="2">表格的頭部信息</td>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="2">表格的腳部信息</td>
<tr>
</tfoot>
<tbody>
<caption>表格標(biāo)題</caption>
<colgroup>
<col span = "1" style="background-color:#ff0000;"></col>
</colgroup>
<tr>
<th>姓名</th>
<th>年齡</th>
</tr>
<tr>
<td>
<table border = "1" width="100%">
<tr>
<td>1</td>
<td>2</td>
</tr>
</table>
</td>
<td>一列二行</td>
</tr>
<tr>
<td>二列一行</td>
<td>二列二行</td>
</tr>
</tbody>
</table>
</body>
</html>
喜歡的小伙伴請關(guān)注我,閱讀中遇到任何問題請給我留言,如有疏漏或錯誤歡迎大家斧正,不勝感激!
學(xué)到這里,相信大家已經(jīng)有獨(dú)立讀懂HTML代碼說明的能力了,明天我會為大家講解16進(jìn)制顏色表示方法。之后會給大家推薦html代碼參考手冊的鏈接。如果您是零基礎(chǔ)的話,學(xué)完16進(jìn)制顏色表示方法后,基本上就可以無障礙的閱讀html代碼參考手冊了,如果閱讀起來還是有困難,請繼續(xù)看后面我為大家講解一些常用元素及屬性的文章,已及html中特殊符號的輸入方法,全部做完后再結(jié)束這套教程。
如果您有任何疑問或不解歡迎關(guān)注并私信我。
HTML序章(學(xué)習(xí)目的、對象、基本概念)——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML是什么?——零基礎(chǔ)自學(xué)網(wǎng)頁制作
第一個HTML頁面如何寫?——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML頁面中head標(biāo)簽有啥用?——零基礎(chǔ)自學(xué)網(wǎng)頁制作
初識meta標(biāo)簽與SEO——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML中的元素使用方法1——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML中的元素使用方法2——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML元素中的屬性1——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML元素中的屬性2(路徑詳解)——零基礎(chǔ)自學(xué)網(wǎng)頁制作
使用HTML添加表格1(基本元素)——零基礎(chǔ)自學(xué)網(wǎng)頁制作
使用HTML添加表格2(表格頭部與腳部)——零基礎(chǔ)自學(xué)網(wǎng)頁制作
使用HTML添加表格3(間距與顏色)——零基礎(chǔ)自學(xué)網(wǎng)頁制作
使用HTML添加表格4(行顏色與表格嵌套)——零基礎(chǔ)自學(xué)網(wǎng)頁制作
16進(jìn)制顏色表示與RGB色彩模型——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML中的塊級元素與內(nèi)聯(lián)元素——零基礎(chǔ)自學(xué)網(wǎng)頁制作
初識HTML中的<div>塊元素——零基礎(chǔ)自學(xué)網(wǎng)頁制作
在HTML頁面中嵌入其他頁面的方法——零基礎(chǔ)自學(xué)網(wǎng)頁制作
封閉在家學(xué)網(wǎng)頁制作!為頁面嵌入PDF文件——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML表單元素初識1——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML表單元素初識2——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML表單3(下拉列表、多行文字輸入)——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML表單4(form的action、method屬性)——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML列表制作講解——零基礎(chǔ)自學(xué)網(wǎng)頁制作
為HTML頁面添加視頻、音頻的方法——零基礎(chǔ)自學(xué)網(wǎng)頁制作
音視頻格式轉(zhuǎn)換神器與html視頻元素加字幕——零基礎(chǔ)自學(xué)網(wǎng)頁制作
HTML中使用<a>標(biāo)簽實(shí)現(xiàn)文本內(nèi)鏈接——零基礎(chǔ)自學(xué)網(wǎng)頁制作
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。