文献 www.manongjc.com/detail/51-l...
js加载机制(假设总是本地脚本index.js下载更快)
- index.js
js
try {
console.log(_.VERSION);
} catch (error) {
console.log('Lodash Not Available');
}
console.log(document.body ? 'YES' : 'NO');
示例1
- index.html
js
<head>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>
<script src="./index.js"></script>
</head>
<body>
</body>
- 控台结果
js
4.17.10
NO
- 结果说明:浏览器加载脚本是采用同步模型的,都会阻塞浏览器的解析器,位于该 script 标签以上的 DOM 元素是可用的,位于其以下的 DOM 元素不可用
示例2 script上加上async
属性
- index.html
js
<head>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js" async></script>
<script src="./index.js" async></script>
</head>
<body>
</body>
- 控台结果
js
Lodash Not Available
YES
- 结果说明:
async
不会阻塞 HTML 解析器, async 脚本每个都会在下载完成后立即执行,无关 script 标签出现的顺序
示例3 script上加上defer
属性
- index.html
js
<head>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js" defer></script>
<script src="./index.js defer></script>
</head>
<body>
</body>
- 控台结果
js
4.17.10
YES
- 结果说明:
defer
不会阻塞 HTML 解析器, - defer 脚本会根据 script 标签顺序先后执行
模块化之前 变量污染,请求多个script脚本性能浪费,依赖模糊
js
// 全局定义变量 容易变量污染
function foo(){
}
function bar(){
}
// Namespace模式: 减少全局变量数量,但还是存在污染问题,还是可以通过对象改写数据,不安全
var myUtils = {
foo:function(){},
bar:function(){}
}
// 匿名闭包(IIFE模式):
var module = (function () {
var age = 9;
var foo = function () {
console.log('age', age);
}
return {
foo,
age
}
})()
module.age++
console.log('module.age', module.age);// 10
module.foo() // 9
// IIFE模式,传入依赖
var module = (function ($, window) {
var age = 9;
var foo = function () {
console.log('age', age);
}
return {
foo,
age
}
})(jQuery, window)
模块化的诞生
目的:为避免全局污染,命名冲突,按需加载,提高复用和可维护性
- 分类:commonjs amd es6
commonjs
- CommonJS期初主要用于Node.js环境,采用同步加载模式,如果想运行于浏览器端,需要运行其编译后的产物
- commonjs表现形式:对外暴露
exports / module.exports
,引入则用var module = require('xxx.js')
js
// module1.js 1个文件可以有多个exports
exports.foo = function(){
console.log('module1---foo')
}
exports.bar = function(){
console.log('module1---bar')
}
// module2.js 1个文件只有1个module.exports
module.exports = {
foo(){
console.log('module2---foo')
}
}
// 入口文件 main.js
var module1 = require('./module1.js')
var module2 = require('./module2.js')
module1.foo()
module2.foo()
//index.html需要运行main.js编译后的产物才不会报错
amd(Requirejs)
- amd 运行于浏览器端,通过异步加载模块,主要解决全局命名空间污染,相互依赖脚本加载顺序问题
- amd表现形式:定义模块define(['依赖项'],function(依赖对象){}),引用则通过requirejs
js
// module1.js (没有依赖的模块)
define(function(){
var age = 9;
function getAge(){
return age;
}
// 暴露
return {
getAge
}
})
// module2.js(有依赖的模块)
define(['module1'], function(module1){
function showAge(){
console.log('module2---showAge', module1.getAge)
}
return {
showAge
}
})
// main.js
requirejs.config({
// baseUrl:'', // 基础路径
paths:{
module1: './module1', // key:define第一个参数的模块命名, 第二个参数是该模块对应的文件路径
module2: './module2'
}
})
requirejs(['module2'], function(module2){
module2.showAge()
})
// index.html require.js是提前准备好的库
<script data-main="./main.js" src="./lib/require.js">
es6模块
- es6模块 模块的导入是静态的,并且模块只有在被引用时才会执行,支持 Tree Shaking, 需要经过编译后运行于浏览器端
- es6表现形式:
export / export default
,引入import {命名一致} from '' / import utils from ''
js
// 导出变量、函数和类
export const variable = 42;
export function add(a, b) { return a + b; }
export class Person { /* ... */ }
// 默认导出
export default function() {
console.log('This is the default export.');
}
// 命名导入
import { variable, add, Person } from './module.js';
// 默认导入
import myFunction from './module.js';
es6模块的延伸(vite开发环境运行快也是基于现代浏览器对ESM的支持)
- 本身es6模块需要经过编译,浏览器运行编译后的产物才不会报import等语法错误,但是基于现代浏览器对##
<script type=module>
的支持,使得浏览器以 ES Module 的方式加载脚本 - 默认情况下 ES 脚本是 defer 的,无论内联还是外联,- 给 script 标签显式指定
async
属性,可以覆盖默认的 defer 行为 - 安全策略更严格,非同域脚本的加载受 CORS 策略限制