>[success] # CommonJS 在node服务器端出现
1. **CommonJS是一个规范**,最初提出来是在浏览器以外的地方使用(创建一个专注于非浏览器(即服务器端、命令行、桌面)功能的通用 JavaScript 库。),并且当时被命名为**ServerJS**,后来为了体现它的广泛性,修改为**CommonJS**,**简称为CJS**,这个规范约定了
1.1. **一个文件就是一个模块**
1.2. **每个模块都有单独的作用域**
1.3. 通过 **modeule.exports也可以exports** 导出成员
1.4. 通过 **require 函数**载入模块
2. Node中对**CommonJS规范进行了支持和实现**
2.1. 在Node中每**一个js文件都是一个单独的模块**
2.2. **这个模块中包括CommonJS规范的核心变量**:exports、module.exports、require
>[info] ## node 模块化
~~~
1.在客户客户端可以通过'sprict' 标签对各个模块进行引入,上一章节演示了演变过程,为了达到属性私有,命名
不重复等问题,利用立即执行函数并且将要暴露的变量挂在到'windows'。
2.'node' 服务端运行环境没有'sprict'引入形式,单纯对其进行挂在全局变量的处理,看起来也不是一个明智的做法,
像案例一这样就不是好的解决方式
3.在浏览器中,顶层的作用域是全局作用域。 这意味着在浏览器中,`var something`将定义新的
全局变量。 在 Node.js 中这是不同的。 顶层作用域不是全局作用域;Node.js 模块内的
`var something`将是该模块本地的,简单的说也就是你在node 环境中定义了一下代码
var a = 1 此时通过'global.a' 也是无法访问到的
~~~
>[danger] ##### 案例一
~~~
1.创建一个结构目录如下:
src
├── index.js
└── example.js
2.这种形式和上个章节讲到的模块演变过程中在浏览器大家通过window 将要暴露出去的变量进行
挂在是一个思路。利用node 的顶层作用域中的global 作为'中介',同样的弊端也一样同名会被
修改
~~~
* example.js
~~~
1.node 每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,
对其他文件不可见。因此下面的 x 和 addX 都是可以理解成私有的外界不能访问
2.将'g' 属性挂在到'global' 全局属性上,此时其他文件可以访问,相对的导致其他文件如果有相同的通过'global'
到处的'g' 属性会被覆盖
~~~
~~~
var x = 5;
var addX = function (value) {
return value + x;
};
global.g = 11
~~~
* index.js
~~~
require('./example.js')
// 正常打印出11
console.log(global.g);
// addX 和 x 属性为私有的 下面运行报错没有该属性
console.log(x)
console.log(addX )
~~~
>[success] # module 和 require
1. CommonJS规范规定,每个模块内部,**module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口**。加载某个模块,其实是加载该模块的**module.exports**属性。
2. require方法用于加载模块。
3. 这种设计和上个章节中模块化演变历程中通过对象导出模块思路很像,首先假设我们在浏览器环境需要模拟一个简单node这类思路,要满足几个简单的条件
3.1. 每个文件定义的var 变量不能在通过window 访问
3.2. 需要可以访问被暴露的对象
~~~
// 一个最简单的思路应用
(function () {
var a = 1
window.module.export = {
name: 18,
}
})()
~~~
但实际上node 模块化比这个要复杂的多
>[danger] ##### node cjs 使用案例
1. node **每一个文件就是一个模块**,**因此文件和文件直接是不能访问**,一个文件叫做**一个模块**,每个模块有属于模块自己的全局变量(**是模块级别的**不是全局的),`__dirname、__filename、exports、module、require()`
2. 其中`exports `和 `module.exports` 都是导出,`require `是导入,**module对象的exports属性是exports对象的一个引用 `exports = module.exports `**,require 导出也是`module.exports`,正因为exports 指向了 module.exports因此我们怎加exports对象属性即`exports .a = 1` 也是可以通过require 导出,但是如果切断了`exports ={ a:1 }`此时**module.exports** 还是为空因此`require`导出也是空
* aa.js
~~~
const a = 1
// 导出a
// exports.a = a
// 这样不可以,切断了exports 和module.exports关联
// exports = {}
// 想导出一个对象
module.exports = {
a,
}
~~~
* bb.js
~~~
// 导出aa 文件模块
const moduleA = require('./aa')
console.log(moduleA.a) // 1
~~~
3. CommonJS模块是对象,是'**运行时加载**',运行时才把模块挂载在exports之上(加载整个模块的所有)模块输出的是'**一个值的拷贝**' (下面案例演示了证明了是一个copy过程)
* 创建aa.js 文件用来导出,此时aa 文件值想要被别的文件使用打破自己模块的壁垒使用`exports `
~~~
const a = 1
setTimeout(() => {
exports.a = 12 // 一秒钟后改变值
}, 1000)
setTimeout(() => {
console.log(exports.a) // 1.5秒后 接收 bb 哪里更改值 打印12
}, 1500)
// 导出a
exports.a = a
~~~
* bb 用来接受aa 模块传来的可以访问的值
~~~
// 导出aa 文件模块
const moduleA = require('./aa')
console.log(moduleA.a) // 1
setTimeout(() => {
console.log(moduleA.a) // 12
}, 1000)
~~~
>[info] ## 了解module
~~~
1.`module.id`模块的识别符,通常是带有绝对路径的模块文件名。
2.`module.filename`模块的文件名,带有绝对路径。
3.`module.loaded`返回一个布尔值,表示模块是否已经完成加载。
4.`module.parent`返回一个对象,表示调用该模块的模块。
5.`module.children`返回一个数组,表示该模块要用到的其他模块。
6.`module.exports`表示模块对外输出的值。
7.这些对象在所有模块中都可用。 以下变量可能看起来是全局的,但实际上不是。 它们只存在于
模块的作用域中,简单的说这些变量也不是直接挂在到global 上也是属于每个模块独立的,并
不是全局统一共享的:
__dirname
__filename
exports
module
require()
8.在执行模块代码之前,Node.js 将使用如下所示的函数封装器对其进行封装通过这样做Node.js
实现了以下几点:
8.1.它将顶层变量(用 var、const 或 let 定义)保持在模块而不是全局对象的范围内。
8.2.它有助于提供一些实际特定于模块的全局变量,例如:
module 和 exports 对象,实现者可以用来从模块中导出值。
便利变量 __filename 和 __dirname,包含模块的绝对文件名和目录路径
// 在重新来看模型
(function(exports, require, module, __filename, __dirname) {
// 模块代码实际存在于此处
});
~~~
>[danger] ##### module
~~~
1.在node 文件中 写入'console.log(module)' 执行打印,下面的打印每个人环境对应的地址是不同,以我的电脑
打印输出如下
Module {
id: '.',
path: '/mnt/d/js-study/easyTest/src',
exports: {},
parent: null,
filename: '/mnt/d/js-study/easyTest/src/index.js',
loaded: false,
children: [],
paths: [
'/mnt/d/js-study/easyTest/src/node_modules',
'/mnt/d/js-study/easyTest/node_modules',
'/mnt/d/js-study/node_modules',
'/mnt/d/node_modules',
'/mnt/node_modules',
'/node_modules'
]
}
~~~
>[danger] ##### 关于exports 变量
~~~
1.在将文件变量导出的时候经常有下面两种写法
module.exports = {
name: 'w'
}
exports.name = "w"
2.'module.exports'属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取
'module.exports'变量,可以理解成对外暴露的变量,即像以前模块化的形式挂在到'windows'变量类似
可以理解成挂在到'module.exports'
3.Node为每个模块提供一个exports变量,指向module.exports,但是不能直接将exports变量指向一个值,
因为这样等于切断了exports与module.exports的联系,举个例子
exports = module.exports
// 切断了联系
exports = {
name: 'w'
}
~~~
* 创建案例
~~~
module.exports = {
name: 'w'
}
console.log(module);
~~~
* 打印结果
~~~
Module {
id: '.',
path: '/mnt/d/js-study/easyTest/src',
exports: { name: 'w' },
parent: null,
filename: '/mnt/d/js-study/easyTest/src/example.js',
loaded: false,
children: [],
paths: [
'/mnt/d/js-study/easyTest/src/node_modules',
'/mnt/d/js-study/easyTest/node_modules',
'/mnt/d/js-study/node_modules',
'/mnt/d/node_modules',
'/mnt/node_modules',
'/node_modules'
]
}
~~~
>[danger] ##### require
1. **require命令**的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的**exports对象**。**如果没有发现指定模块,会报错**。
1.1. 如果require(x),**x导入是核心模块**(path、http)会直接返回核心模块,并且停止查找
1.2. 如果require(x),**X是以 ./ 或 ../ 或 /(根目录)开头的**,**如果有后缀名**,按照后缀名的格式查找对应的文件,**如果没有后缀名**,会按照如下顺序,**直接查找文件X-》查找X.js文件-》查找X.json文件 -》查找X.node文件**, 没有找到文件就当作文件夹处理会依次找文件夹下的 **X/index.js文件=》X/index.json文件=》X/index.node文件**,都没找到就报错
1.3. **直接是一个X(没有路径),并且X不是一个核心模块** 就会去**node_modules** 中找
2. 通过下面例子可以看出'**require**' 命令将模块的'**module.exports**' 进行导出使用
* 举个例子
~~~
var example = require('./example.js');
example
// {
// message: "hi",
// say: [Function]
// }
// 如果模块输出的是一个函数 而不是一个对象
module.exports = function () {
console.log("hello world")
}
require('./example2.js')()
~~~
* 案例二打印导入的module
~~~
// 忽略两个导入文件中的具体内容
require('./firstModules.js')
require('./second.js')
console.log(module)
~~~
~~~
1.可以看到当前文件的module 变量中children 明确记录了require 导入一些具体详情
<ref *1> Module {
id: '.',
path: 'd:\\test-js\\testjs\\src',
exports: {},
parent: null,
filename: 'd:\\test-js\\testjs\\src\\index.js',
loaded: false,
children: [
Module {
id: 'd:\\test-js\\testjs\\src\\firstModules.js',
path: 'd:\\test-js\\testjs\\src',
exports: [Object],
parent: [Circular *1],
filename: 'd:\\test-js\\testjs\\src\\firstModules.js',
loaded: true,
children: [],
paths: [Array]
},
Module {
id: 'd:\\test-js\\testjs\\src\\second.js',
path: 'd:\\test-js\\testjs\\src',
exports: [Object],
parent: [Circular *1],
filename: 'd:\\test-js\\testjs\\src\\second.js',
loaded: true,
children: [],
paths: [Array]
}
],
paths: [
'd:\\test-js\\testjs\\src\\node_modules',
'd:\\test-js\\testjs\\node_modules',
'd:\\test-js\\node_modules',
'd:\\node_modules'
]
}
~~~
>[info] ## cjs 模块加载过程
1. 模块在被第一次引入时,模块中的js代码会被运行一次
2. 模块被多次引入时,会缓存,**最终只加载(运行)一次**,**每个模块对象module都有一个属性:loaded,为false表示还没有加载,为true表示已经加载**
* 了解webpack 可能发现,webpack 打包后也是cjs,那不是和结论冲突了,webpack打包后例如你代码开发阶段是分成了100个模块但是打包后webpack **all in one** 特性最后除非是特别设置 那整体打包后往往只有一个文件,在一个文件内读取在一个文件做了模块导入导出
![](https://img.kancloud.cn/c8/96/c896a7f167ba355cf3fe24b2d0dd3cd0_878x760.png)
3. 关于循环加载 ??? 后续查
>[info] ## cjs 总结
1. CommonJS**加载模块是同步的**,只有等到对应的模块加载完毕,当前模块中的内容才能被运行,**在服务器加载的js文件都是本地文件**,但相比**在浏览器**加载js文件需要先从服务器将**文件下载下来**,**之后再加载运行**,采用同步的就意味着后续的js代码都无法正常运行,即使是一些简单的DOM操作
>[info] ## 实际原理和构想
[「万字进阶」深入浅出 Commonjs 和 Es Module
](https://juejin.cn/post/6994224541312483336)
[读懂CommonJS的模块加载
](https://juejin.cn/post/6844903685466619911#heading-3)
>[info] ## 如何在浏览器运行cjs
[# 浏览器加载 CommonJS 模块的原理与实现](https://www.ruanyifeng.com/blog/2015/05/commonjs-in-browser.html)
>[info] ## 参考地址
[# CommonJS规范](https://javascript.ruanyifeng.com/nodejs/module.html#)
- HTTP -- 初始
- HTTP -- 什么是HTTP
- HTTP -- 相关技术
- HTTP -- 相关的协议
- Emmet -- 语法
- HTML -- 补充
- iframe -- 补充
- checkbox 和 radio 细节
- form -- 补充
- html -- html5
- html -- 视频音频
- html -- html5 data-* 全局属性
- css -- 重学
- css -- 单位
- css 知识补充 -- 导入
- css -- 颜色补充
- css --继承性
- css - 元素隐藏
- 标签元素--块/行内/行内块
- css -- 盒子阴影 – 在线查看
- css -边框图形
- css -- Web字体
- css -- 精灵图
- css -- 属性补充
- text-align -- 内容
- line-height -- 行高
- 文字换行
- overflow-溢出
- css -- 选择器
- css -- 伪元素
- css -- 伪类选择器
- 设置高度宽度 和 盒子模型
- css -- 文字溢出
- css -- white-space / text-overflow
- css -- 定位
- css -- 浮动
- 浮动 -- 案例
- flex -- 布局
- flex -- 解决等距布局
- flex -- 内容居中
- flex -- 导航栏
- Grid -- 布局
- css -- transform 形变
- css -- 垂直水平居中
- css -- transition 动画
- css -- Animation 动画
- css -- vertical-align
- css -- 函数
- css -- 媒体查询
- 重学案例
- 重新 -- 鼠标常见样式
- 重学 -- 透明边框background-clip
- 重学 -- 多重边框box-shadow
- css -- 预处理器
- 移动端适配
- 前端结构目录说明
- 浏览器 加载过程
- 回流和重绘
- 杂七杂八 -- 小工具方法
- npm包比较网站
- IP定位
- 通过useragent获取手机品牌型号
- 自启本地服务
- BOM -- 常用知识点记录
- window -- 认识
- windows -- 大小滚动
- BOM -- Location
- BOM -- URLSearchParams
- BOM -- history
- BOM -- navigator 和 screen
- 前端存储 -- Cookie、LocalStorage、IndexedDB
- DOM -- 知识总结
- DOM -- 获取元素
- DOM -- 节点属性
- DOM -- 元素属性
- 获取元素style的读取 - getComputedStyle
- DOM -- 元素的操作
- DOM -- 元素大小和滚动
- DOM -- 小练习
- Event -- 事件
- event -- 事件对象
- event -- 案例
- event -- 做一个树
- js -- ajax
- 封装一个ajax
- ajax -- 文件上传
- 倒计时抢购案例
- axios -- 封装
- 跨域
- 前端 -- Fetch API
- js -- 基础篇
- 数据类型
- 数据类型检测
- 类型小知识
- 原始类型的包装类
- 类型转换
- delete -- 运算符
- Date -- 对象
- 函数参数传递
- 对象某个属性不存时候判断
- 操作符
- 函数变量传值
- 访问对象通过点和[]
- 和if等同的一些写法
- for -- 执行顺序
- JS -- 执行过程
- JS中的堆(Heap)栈(Stack)内存
- JS -- 执行上下文
- Js -- ES3 和 ES5+ 后
- let const var
- ES3 -- 案例思考
- 闭包概念
- 浅拷贝和深拷贝
- JS -- 严格模式
- js -- 数组篇
- Array -- 数组基础
- Array -- 小常识检测数组
- Array -- 小技巧将数组转成字符串
- Array -- 自带方法
- Array -- 数组插入总结
- Array -- every是否都符合巧用
- js--Function篇
- Function -- length 属性
- Function -- arguments
- Function -- 也是对象
- Function -- new Function 创建方法
- Function -- 函数作为返回值
- Function -- callee
- 匿名函数
- Function -- 闭包
- 闭包内存查看
- 闭包 -- 使用的案例
- 闭包 -- 使用的案例
- 箭头函数不适用场景
- js -- this、call、apply
- this -- 指向
- call、apply -- 讲解
- 总结 -- this
- 思考题
- Object -- 数据属性/访问器属性
- 新增关于对象方法
- js -- 面向对象
- 对象到底是什么
- 到底什么是js的对象
- js --prototype、__proto__与constructor 1
- JS es5继承
- JS 中的原型继承说明
- JS -- Object是所有类的父类
- 总结
- Object -- 函数构造函数
- Object -- 手动实现一个new
- js -- 函数式编程(目前只是了解后面需要更多时间深入)
- 了解 -- 高阶函数
- 了解 -- 纯函数
- 了解 -- 函数柯里化
- 柯里化 -- 知识点
- 了解 -- 函数组合
- 了解 -- 函子
- js--小知识点
- url -- 将get请求连接参数变成对象
- event -- 一个函数处理多个事件
- try -- 处理异常
- Error -- 前段报错信息传给后台
- JSON -- 序列化
- return -- 返回true和false
- for -- 循环里初始化变量
- 命名 -- get和set怎么命名
- 链式调用
- 利用递归代替循环思路
- JS -- 技巧篇
- 技巧 -- 代码规范
- 技巧 -- 惰性载入函数
- 技巧 -- 防抖函数
- 技巧 -- 节流函数
- 插入补充(防抖/节流)
- 技巧 -- 定时器的使用
- 技巧 -- 回调函数
- 技巧 -- 分时函数
- 技巧 -- 除了return 我怎么跳出多层循环
- 技巧 -- switch 还是 if-else?
- 技巧 -- 将字符串转成对象
- 技巧 -- 函数转换
- 技巧 -- 工作记录数组对象中相同项
- JS -- 数组小文章总结
- 数组类型判断
- includes 和 indexOf
- for ... in,for ... of和forEach之间有什么区别
- map、filter、reduce、find 四种方法
- 多种形式处理数组思考方式
- for...in 和 Object.keys
- 各种知识点集合文章
- 创建数组 -- 总结
- 数组去重 -- 总结
- 获取数组中不重复的元素 -- 总结
- 比较两个数组元素相同 -- 总结
- 出现频率最高的元素 -- 总结
- 两数组交集 -- 总结
- 两数组差集 -- 总结
- 工具方法 - 总结
- 扁平化数组
- JS -- 数组技巧篇 30s
- 30s Array -- 创建数组篇(一)
- 30s Array --过滤(查询)篇章(一)
- 30s Array --过滤篇章(二)
- 30s Array -- 新增篇(一)
- 30s Array--比较篇(一)
- 30s Array -- 分组篇(一)
- 30 Array -- 删除篇(一)
- 30s Array-- 其他篇(一)
- 30s Array -- 个人感觉不常用篇章
- JS -- 对象技巧篇30s
- 30s Object -- 查(一)
- 30s Object -- 增(一)
- 30s Object -- 工具类型小方法
- 30s Object -- 跳过没看系列
- ES -- 新特性
- 变量篇章
- 变量 -- let/const/var声明
- 变量 -- 词法声明和变量声明
- 变量 -- var x = y = 100
- 变量 -- a.x = a = {n:2}
- 带标签的模板字符串
- 函数篇章
- 函数 -- 参数篇章
- 函数 -- 只能通过new创建实例
- 函数 -- 箭头函数
- 函数 -- 尾调优化
- 对象篇章
- 对象 -- 字面量写法的优化/新增方法
- 赋值篇章
- 解构赋值 -- 简单概念
- 解构赋值 -- 对象解构
- 解构赋值 -- 数组解构
- 解构赋值 -- 函数参数
- Symbol 属性
- Set 和 Map
- Set -- 去重/交、并、差集
- Map -- 集合
- 类class篇章
- ES6 和 ES5 的写法比较
- es6 -- mixin函数继承
- es6 -- 创建一个接口
- ES5 VS ES6 -- class 编译
- 数组新功能
- 创建/转换 -- 数组
- 迭代器和生成器
- es6 -- 迭代器
- es6 -- 生成器
- for-of 循环的是什么
- 做一个异步执行器
- 代理和反射
- Proxy -- 使用
- Reflect -- 使用
- Proxy 和 Reflect 结合使用
- 运算符 -- 展开运算符
- ES7 -- 新特性
- ES8 -- 新特性
- ES9 -- 新特性
- ES10 -- 新特性
- ES11 -- 新特性
- ES12 -- 新特性
- ES13 -- 新特性
- ES6 和 ES8 的异步
- js -- 异步、同步、阻塞、非阻塞
- js -- 如何做到异步的
- js -- 引擎线程,GUI渲染线程,浏览器事件触发线程
- js -- 如何通过事件循环运行异步
- js -- 误区任务回调就是异步
- js -- 宏任务和微任务
- 参考文章推荐阅读
- js -- callback 还是 promise
- js -- Promise 初识
- js -- 自己实现一个Promise
- js -- Promise 更多用法
- 再来看setTimeout 和for
- js -- ES8 异步 async await
- js -- 红绿灯问题
- js -- 倒计时
- 异步图片预加载
- 手动触发异步
- 异步题
- JS -- 模块化
- CommonJS -- 在node服务器端
- AMD 模块化规范
- ES Modules
- ES Modules -- 使用特点
- import 和 export 使用
- export 和 import -- 执行
- 其他用法
- systemjs
- 对比区别
- 使用babel 或webpack进行模块打包
- Jq -- 细节
- JS -- 性能优化
- 图片预加载
- js -- 正则
- 设计原则和编程技巧
- 设计原则 -- 单一职责
- js -- 设计模式
- 设计模式 -- 简单理解
- 一、鸭子类型
- 1.1 多态概念
- 1.2 小章节记录 -- 封装
- 1.3.多态和鸭子
- 设计模式 -- 单例模式(透明、惰性)
- 单例模式 -- js单例和惰性单例
- ES6 -- 写单例模式
- 设计模式 -- 策略模式
- 策略模式 -- 表单验证
- 策略模式 -- 表单验证多规则
- 策略模式和多态区别
- 设计模式 -- 代理模式(代理被代理相同方法)
- 代理模式 -- 分类
- 代理模式 -- 总结
- 设计模式 -- 迭代器模式
- 设计模式 -- 观察者(发布订阅)模式
- 观察者 和 发布订阅区别
- 发布订阅模式 -- 现在通用模型
- 发布订阅模式 -- 书中实际案例
- 发布订阅模式--全局发布订阅对象
- 发布订阅模式 -- vue
- 设计模式 -- 命令模式(对象方法拆分指令)
- 命令模式 -- js 自带
- 命令模式 -- 撤销和重做
- 命令模式 -- 命令和策略的区别
- 设计模式 -- 组合模式(树)
- 组合模式 -- 拆分了解
- 组合模式 -- java角度
- 组合模式 -- 书中案例扫描文件夹
- 组合模式 -- 注意点
- 组合模式 -- 引用父对象(书中案例)
- 组合模式 -- 小的案例
- 组合模式 -- 总结
- 设计模式 -- 模板方法(抽象类继承)
- 模板方法 -- 前端角度思考
- 模板方法 -- java 思考方式
- 模板方法 -- js没有抽象类
- 模板方法 -- 钩子方法
- 模板方法 -- 用js方式来实现
- 设计模式 -- 享元模式
- 享元、单例、对象池
- 享元模式 -- 前端角度思考
- 享元模式 -- 代码案例
- 享元模式 -- 适用和内外部状态
- 额外扩展 -- 对象池
- 设计模式 -- 职责链模式
- 职责链 -- 前端角度
- 职责链和策略
- 职责链 -- 异步的职责链
- 职责链 -- AOP
- 职责链 -- 改造迭代器模式的代码
- 设计模式 -- 中介者模式
- 中介者模式 -- 前端角度(一)
- 中介者模式 -- 前端角度(二)
- 中介者模式 -- 前端角度(三)
- 中介者模式 -- 总结
- 设计模式 -- 装饰者模式
- 装饰者模式 -- es5 解决方案
- 装饰器模式 -- 和代理模式区别
- 设计模式 -- 状态模式
- 状态模式 -- 前端思想
- 状态模式 -- 文件上传案例
- 状态模式 -- 和策略模式的区别
- 设计模式 -- 适配器模式
- js -- 代码重构
- 重构 -- 方法封装
- 重构 -- 抽象函数
- 高阶函数 -- 范式
- 状态管理方案
- Node -- 学习篇
- Node -- 服务端运行的js
- node -- Global(全局成员)
- node -- Buffer缓冲器
- node -- 路径操作
- node -- 文件的读写
- node -- 目录操作
- node -- HTTP
- HTTP -- 响应头常见类型
- HTTP -- 处理Get
- HTTP -- 处理Post
- HTTP -- 简单的案例
- Express
- Express -- 中间件
- Express -- 处理post/get
- Express -- 模板引擎
- Express -- 简单案例和目录搭建
- Express -- 数据库service.js
- Express -- json/url
- Express -- 配合数据库
- 配合数据库 -- 简单的登录
- npm -- js包管理
- npm -- 淘宝镜像
- nrm -- 更多镜像的选择
- yarn -- 包管理
- yarn -- 清理缓存
- WebPack -- 模块化开发
- webPack -- 安装、使用、打包
- webPack -- 使用配置文件
- 延伸 -- base64图片优劣
- webPack -- 完整配置