背景&准备工作
1. 背景
最近接到一个需求,要求对旧项目进行改造一些新功能,根据以往的经验,我认为这种需求应该相对简单,花费不了多长时间,于是对这个需求的交付时间就比较随意了。
事实上,当我接触这个项目时,能隐约地感受到前辈们写代码的历史文化与风格,也看到了一些奇怪的语法,不像是 js 自带的,其中部分代码写的不像现在这样规范,这个可以理解,毕竟那时还不流行这些概念。
废话不多说,本文所碰到的技术就是标题上写的,如果你刚好也接触到类似这种技术的项目,且不是很清楚这些技术是用来干嘛的,不妨往下读。如果你是刚好看到标题进来的,了解即可;毕竟相对于现在的前端发展来说,这些技术都算是过时了。
2. 准备工作
-
新建一个项目,比如
nsg-example
。 -
结构如下:
这里的 app.js 文件后面会用到,先提前创建好。 -
index.shtml
内容如下:
<!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>
<div class="container">
<h2>Hello,world!</h2>
</div>
</body>
</html>
因为要用到 ssi ,得用
.shtml
后缀格式来新建文件,剩下的与普通的 html 没区别。
- 本地打开
index.shtml
,如图:
一、nginx ssi
1. nginx 准备工作
在使用 ssi 功能前,我们得安装 nginx。
下载地址:https://nginx.org/en/download.html
选择稳定版即可:
下载完后解压至你想要存放的目录,结构如下:
2. 使用 nginx 给项目启动 web 服务器
在安装 nginx 的目录下有个 nginx.exe
,双击后你会看到有个小黑窗口一闪而过,这很正常,现在访问:127.0.0.1
,如图:
说明我们的 web 服务器启动完成了。
接下来,我们要实现访问 http://nsg.example.com/index.shtml
来映射到本地项目的 index.shtml 文件。
这里分几个步骤:
- 找到 nginx 里的
nginx.conf
文件
- 将
server
代码块里的内容替换成:
server {
# 监听端口
listen 80;
# 设置域名,由于是本地环境,这里还要设置 hosts 文件,线上则不用考虑这个问题 。
server_name nsg.example.com;
# 设置访问根目录,换成你的项目路径即可。
root D:\\Work\\Projects\\nsg-example;
location / {
# 访问 root 目录下对应目录。
try_files $uri $uri/ =404;
}
location = /50x.html {
root html;
}
}
注意:对于 root 路径问题,在 windows 中,可以用一条或者两条下划线,比如 D:\Work\Projects
,但是,如果目录名存在 -
分隔符号的,则必须用 \\
两条下划线,否则 nginx 只会解析到 D:Work\Projects
这一层导致,这样会导致匹配不到我们的项目目录,不信的可以自己试试,这里的案例项目名是有分隔符的,正确的路径写法是: D:\\Work\\Projects\\nsg-example
。
如果不熟悉 nginx 配置的朋友,不妨参考我前面写过的:Nginx & 详细举例 location -> index、return、rewrite、try_files、alias 各个属性的含义和注意事项
- 配置
hosts
文件以支持 Nginx 里的 server_name 域名,路径一般放在C:\Windows\System32\drivers\etc\hosts
,打开 hosts 文件,新增下面这一行:
127.0.0.1 nsg.example.com
- 以管理员身份打开 cmd 执行以下命令表示刷新 DNS 缓存。
ipconfig /flushdns
- 在 nginx 目录下执行以下命令,表示重新加载配置并启动 nginx。
nginx -s reload
- 现在,我们来访问
http://nsg.example.com/index.shtml
如图:
3. 什么 ssi
ssi
是 Nginx 提供的一种响应时将 ssi 命令转换为指定的内容。具体来讲,我们可以使用 ssi 提供的命令来完成想要的渲染结果。比如,我们可以在 html 里面引入其它的 html 文件,通过 ssi 提供的条件语句命令来动态现实不同内容,它跟“模板引擎”类似,但我认为,它更多的是作为动态引入文件+条件命令而存在的。
4. 使用 ssi
首先,在 nginx 的 nginx.conf
下开启 ssi
功能:
location / {
ssi on; # 开启 ssi
try_files $uri $uri/ =404;
}
配置后在 nginx 目录下重载配置即可:
nginx -s reload
ssi
提供了许多命令,感兴趣的同学可以参考文档。
这里我们举一些比较常见的,比如 include 指令表示引入文件。
首先我们可以新建一个 header.shtml
,内容如下:
<!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>Hello, Header!</h1>
</body>
</html>
在原来的 index.shtml
通过 ssi 指令引入:
<body>
<!--# include virtual="/header.shtml" -->
<div class="container">
<h2>Hello,world!</h2>
</div>
</body>
解释:!--# ... -->
是作为 ssi
唯一识别符号,里面的 virtual
是 include 提供的参数之一,表示引入一个内置请求,这个请求除了 .shtml 还支持 xxx.php xxx.jsp 诸如请求格式,前提是你得通过 nginx 配置代理转发交给 FastCGI/uwsgi/SCGI/gRPC 网关处理。
现在重新访问:http://nsg.example.com/index.shtml
即可看到指令被解析并执行:
二、seajs 框架
1. 什么是 seajs
seajs
与 ssi
两者独立存在,无任何关联。
背景:比如你在 utils.js
定义了 getName 函数,现在有别的同事想在 getName 改造一些功能,但为了避免破坏 getName 的通用性以及重命名,就不得不基于 getName 新建一个别名如 getUserName 来充当,这样会导致当项目逐渐庞大时,将造成开发人员的可读性和维护性的困扰。
seajs
就是为了解决上面这些原始函数命名冲突和规范问题。
它的原理很简单,通过模拟 Node.js CMD 来实现模块化语法,核心点就是每个模块
提供了两个参数:exports / require 负责导出和引入。
2. 使用 seajs
在原来的 index.shtml
基础上,通过 cnd 的方式引入 seajs
,如下:
<!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>
<!--# include virtual="/header.shtml" -->
<div class="container">
<h2>Hello,world!</h2>
</div>
<!-- 引入 seajs -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/seajs/2.1.1/sea.js"></script>
<script>
// seajs 的简单配置
seajs.config({
base: "./src/modules", // 告诉 seajs 模块存放路径,后续 require 引入时节省前缀。
});
// 加载模块
seajs.use("./src/app.js", function(module) {
module.app.date.getCurrentTime()
});
</script>
</body>
</html>
如代码所示,先用 seajs.config 初始化配置,再用 seajs.use 引入 app.js 模块,这个 module.app.date.getCurrentTime
函数是怎么来的?别急,我们一步一步来:
首先 app.js
是一个模块,它在里面引入了其它两个模块,代码如下:
define(function (require, exports) {
// 直接引入模块因为前面在 seajs 已通过 config 配置好模块路径了。
var date = require('date.js');
var user = require('user.js');
exports.app = {
date: date,
user: user,
};
});
解释:从代码中可以看到使用了 define
,它是 seajs 提供的全局函数,该函数的第一个参数是个回调函数,回调函数里提供了两个核心的 require/exports
参数函数。通过 require
引入了 date.js & user.js 模块,再通过 exports
将这俩模块包括起来以 app 对象导出去。
现在我们到 src 下新增 modules
目录(seajs.config 已配置的),里面定义 app 引入的俩模块代码,如下:
// date.js
define(function (require, exports) {
exports.getCurrentTime = function() {
return alert(Date.now());
}
});
// user.js
define(function (require, exports) {
exports.getUserNameById = function(id) {
if (id === 10000) {
return 'Jack';
}
return 'Guest'
}
});
现在来看看效果:
3. 常用的 API
实际上,我们在上面已经用过这些 API 了,常见的 API 并不多,这里再汇总下:
define
- require 引入其它 define
- require.async 异步引入 define
- exports 导出对象、方法、等
seajs.use
使用 defineseajs.config
初始化 seajs
4. 总结
- seajs 解决了命名空间问题
- seajs 通过 define 函数提供的 require & exports, 来定义模块和引入其它模块
- seajs 通过 use 来引入 define 定义的模块。