前端工程化
- 前端工程化就是通过各种工具和拘束,提升前端开发效率的过程。
NOde.js
- 官方网址:nodejs.org/
浏览器内核的组成部分
- 渲染引擎:用来做内容渲染,比如html、css渲染。
- js引擎:渲染JavaScript
node.js的作用
- Node.js适合开发服务器端的应用层(BFF)
- 为网站,APP,小程序等提供数据服务
- Node.js适合用于开发前端方向的各种工具
- 各种前端工程化的工具
- Node.js可以用来做桌面应用开发
- 各种跨平台的桌面应用开发
全局对象
Node.js下的全局对象是global。
- 在
交互模式
下,声明的变量和函数都属于global
。- 例如:var a = 1; global.a可以放文到 a 的值。
- 在
脚本模式
下,声明的变量和函数都不属于global
。- 例如:var a = 1; global.a访问不到a的值。
全局函数
- JavaScript语言提供的全局函数,在Node.hs下依然可用。
setImmediate()
- 表示立即执行事件,参数是一个函数,表示执行事件的函数。
- setImmediate()是在队列事件中执行,与setTimeout()等函数都是在事件队列中执行,相当于异步执行。
- 当主线程执行完之后,才会执行任务队列中的任务。
setImmediate()与process.nextTick()的区别
- setImmediate()是存在于任务队列中,并且是任务队列中有限执行的事件。
- process.nextTick()是存在于主进程中,并且在主进程中是最后执行的。
- 任务队列中的任务需要等主进程中的任务执行结束后才会执行,也就是说理论上需要等process.nextTick()执行完成之后才会执行setImmediate()事件任务。
Node.js模块
模块是Node.js应用程序的基本组成部分,大部分前端工程化的工具,是以模块的形式存在的。
Node.js中的模块可以划分为内置模块
、自定义模块
、第三方模块
三类。
内置模块
是官方提供的,跟随Node.js一起安装。
自定义模块
是自己定义的模块。
第三方模块
社区维护的,需要单独下载才能使用。(https://www.npmjs.com/)
内置模块
内置模块-process
- process 对象是一个全局变量,提供了有关当前 Node.js 进程的信息并对其进行控制。 作为全局变量,它始终可供 Node.js 应用程序使用,无需使用 require()。 它也可以使用 require() 显式地访问:
const process = require('process');
- 获取操作系统:process.arch
- 获取操作系统平台:process.platform
- 获取当前工作目录:process.cwd()
- 获取环境变量:process.env
- 自定义环境变量:process.env.NODE_ENV = "develop"
- 获取进程编号:process.pid
- 杀死进程:process.kill("进程编号")
内置模块-path
- 在使用之前,需要通过require引入
const path = require("path")
- 获取当前文件所在的路径:console.log(
__dirname
) - 获取当前文件的完整路径:console.log(
__filename
) - 获取文件的扩展名:path.extname(
__filename
) - 获取路径中的目录部分:path.dirname(
__filename
) - 获取路径中的文件名:path.basename(
__filename
) - 合并路径:path.join("", "")
内置模块-fs
- fs提供了文件操作的API。
- 文件操作
- 目录操作
- 使用之前,需要通过require引入。
写入文件 writeFile()
写入文件 writeFile(),清空写入
- 语法:fs.writeFile(file, data[, options], callback)
- 参数说明:
- file <string> | <Buffer> | <URL> | <integer> 文件名或文件描述符。
- data <string> | <Buffer> | <TypedArray> | <DataView> 写入内容。
- options <Object> | <string>
- encoding <string> | <null> 默认值: 'utf8'。
- mode <integer> 默认值: 0o666。
- flag <string> 参见文件系统 flag 的支持。 默认值: 'w'。
- callback <Function> 回调函数。
- err <Error>
- 示例:
const fs = require("fs");
const path = require("path");
// 构造文件路径
let fileName = path.join(__dirname, "1.txt");
// 写入文件
fs.writeFile(fileName, "写入内容", { flag: "a+" }, (err) => {
// 如果有异常,则抛出异常
if (err) throw err;
console.log("写入成功");
});
- 如果 options 是字符串,则它指定字符编码
fs.writeFile('文件.txt', 'Node.js 中文网', 'utf8', callback);
追加的方式写入文件 appendFile()
以追加的方式写入文件
- path <string> | <Buffer> | <URL> | <number> 文件名或文件描述符。
- data <string> | <Buffer>
- options <Object> | <string>
- encoding <string> | <null> 默认值: 'utf8'。
- mode <integer> 默认值: 0o666。
- flag <string> 参见文件系统 flag 的支持。默认值: 'a'。
- callback <Function>
- err <Error>
如果文件不存在,则创建文件
- err <Error>
fs.appendFile('文件.txt', '追加的数据', (err) => {
if (err) throw err;
console.log('数据已被追加到文件');
});
如果 options 是字符串,则它指定字符编码:
fs.appendFile('文件.txt', '追加的数据', 'utf8', callback);
读取文件 readFile()
- 语法:fs.readFile()path[, options], callback
- 参数说明:
- path <string> | <Buffer> | <URL> | <integer> 文件名或文件描述符。
- options <Object> | <string>
- encoding <string> | <null> 默认值: null。
- flag <string> 参见文件系统 flag 的支持。默认值: 'r'。
- callback <Function>
- err <Error>
- data <string> | <Buffer>
- 示例:
fs.readFile(fileName, "utf8", (err, data) => {
if (err) throw err;
console.log(data);
});
- 如果不写"utf8",返回的data是一个二进制数据,是以十六进制展示,如果想正常展示,需要使用
tostring()
方法。 - fs.readFile() 函数会缓冲整个文件。 若要最小化内存成本,则尽可能选择流式(使用 fs.createReadStream())。
删除文件 unlink()
删除文件
- path <string> | <Buffer> | <URL>
- callback <Function>
- err <Error>
const fs = require("fs");
const path = require("path");
let fileName = __dirname;
fs.unlink(path.join(fileName, "err.txt"), (err) => {
if (err) throw err;
console.log("删除文件成功");
});
创建目录 mkdir()
创建目录:
fs.mkdir(path[, options], callback)
参数:path <string> | <Buffer> | <URL>
-
options <Object> | <integer>
- recursive <boolean> 默认值: false。
- mode <string> | <integer> 在 Windows 上不支持。默认值: 0o777。
-
callback <Function>
- err <Error>
// 创建 `/目录1/目录2/目录3`,不管 `/目录1` 和 `/目录1/目录2` 是否存在。
fs.mkdir('/目录1/目录2/目录3', (err) => {
if (err) throw err;
});
midirSync()
同步创建文件:
fs.mkdirSync(path[, options])
参数:path <string> | <Buffer> | <URL>
-
options <Object> | <integer>
- recursive <boolean> 默认值: false。
- mode <string> | <integer> 在 Windows 上不支持。默认值: 0o777。
删除目录 rmdir()
注意:rmdir只能删除空目录
语法:
fs.rmdir(path[, options], callback)
参数:path <string> | <Buffer> | <URL>
-
options <Object>
- maxRetries <integer> 如果遇到 EBUSY、 EMFILE、 ENFILE、 ENOTEMPTY 或 EPERM 错误,则 Node.js 会重试该操作(每次尝试时使用 retryDelay 毫秒时长的线性回退等待)。 此选项表示重试的次数。 如果 recursive 选项不为 true,则此选项会被忽略。 默认值: 0。
- recursive <boolean> 如果为 true,则执行递归的目录删除。 在递归模式中,错误不会被报告(如果 path 不存在),并且操作会被重试(当失败时)。 默认值: false。
- retryDelay <integer> 重试之间等待的时间(以毫秒为单位)。 如果 recursive 选项不为 true,则此选项会被忽略。 默认值: 100。
-
callback <Function>
- err <Error>
文件重命名 rename()
异步地把 oldPath 文件重命名为 newPath 提供的路径名。 如果 newPath 已存在,则覆盖它。 除了可能的异常,完成回调没有其他参数。
语法:
- fs.rename(oldPath, newPath, callback)
参数:
- oldPath <string> | <Buffer> | <URL>
- newPath <string> | <Buffer> | <URL>
- callback <Function>
- err <Error>
fs.rename('旧文件.txt', '新文件.txt', (err) => {
if (err) throw err;
console.log('重命名完成');
});
读目录 readdir()
读取目录,返回一个数组,数组中保存当前目录下的文件/文件夹名称。
语法:
- fs.readdir(path[, options], callback)
参数:
- path <string> | <Buffer> | <URL>
- options <string> | <Object>
- encoding <string> 默认值: 'utf8'。
- withFileTypes <boolean> 默认值: false。
- callback <Function>
- err <Error>
- files <string[]> | <Buffer[]> | <fs.Dirent[]>
读取目录的内容。 回调有两个参数 (err, files),其中 files 是目录中文件的名称的数组(不包括 '.' 和 '..')。
可选的 options 参数可以是字符串(指定字符编码)、或具有 encoding 属性(指定用于传给回调的文件名的字符编码)的对象。 如果 encoding 被设置为 'buffer',则返回的文件名会作为 Buffer 对象传入。
如果 options.withFileTypes 被设置为 true,则 files 数组会包含 fs.Dirent 对象。
判断当前文件是否是目录 stat()
语法:
- fs.stat(path[, options], callback)
参数:
- path <string> | <Buffer> | <URL>
- options <Object>
- bigint <boolean> 返回的 fs.Stats 对象中的数值是否为 bigint 型。 默认值: false。
- callback <Function>
- err <Error>
- stats <fs.Stats>
回调有两个参数 (err, stats),其中 stats 是 fs.Stats 对象。
若要只检查文件是否存在,但没有更多的操作,则建议使用 fs.access()。
.isDirectory() // 是否是目录
.isFile() // 是否是普通文件
路径是否存在 existsSync()
用于检查文件路径是否存在:
- fs.existsSync(path)
参数:
- path <string> | <Buffer> | <URL>
- 返回: <boolean>
if (fs.existsSync('文件')) {
console.log('该路径已存在');
}
文件流
文件操作可以是缓冲的方式,也可以是文件流的方式,缓冲的方式就是比如在写入文件的时候,首选读取源文件,然后将源文件读取到内存缓冲中,当缓存中的文件溢出的时候,在将内容写入到新的文件中,上述的文件写入方式就是通过缓冲方式实现。
文件流表示将读取的文件和写入的文件通过一个管道连接,直接将要读取的文件内容写入到新的文件中。
特点:
- 内存效率提高
- 无需加载大量数据。
- 流把大数据切成小块,占用内存更少。
- 时间效率提高
- 获取数据后立即开始处理。
- 无需等到内存缓冲填满。
// 1.创建读取流
const readStream = fs.createReadStream("文件路径");
// 2.创建写入流
const writeStream = fs.createWriteStream("文件路径");
// 3.把读取流通过管道传给读取流
readStream.pipe(writeStream)
内置模块-http
const http = require("http");
// 创建服务
// req = request 请求
// res = response 响应
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.end("你好");
});
// 发布web服务
const port = 1234;
const host = "localhost";
server.listen(port, host, () => {
console.log(`服务器运行在:http://${host}:${port}`);
});
自定义模块
- Node.js中每个单独的
.js
文件,就是一个模块。 - 每个模块中都有一个
module
变量,其代表当前模块。 -
module
中的exports
属性时对外的接口。- 只有导出(module.exports)的属性或方法才能被外部调用
- 未导出的内容是模块私有,不能被外部访问。
- 使用时需要用
require
引入。引入的时候需要将完整的路径写入。可以省略后面的.js
// modules.js
function info() {
return "自定义模块";
}
module.exports = {
info,
};
// app.js
const info = require("./modules");
console.log(info.info());
模块的加载逻辑
以路径开头引入:
-
文件模式:
- 可以直接通过
require("文件路径")
引入,后缀名可以省略不写。
- 可以直接通过
-
目录模块:
- 通过
require("目录路径")
,然后引入入口文件。 - 默认入口文件是
index.js
,在目录路径中直接写入路径的时候,默认在这个目录下寻找index.js
这个文件。 - 如果没有
index.js
这个文件,可以在目录下创建一个package.json
文件,在文件中通过main
属性指定入口文件。 - package.json是目录模块中的扫描文件。
- 通过
// package.json
{
"main": "modules.json"
}
不以路径开头引入:
- 文件模式:
- 引入官方的内置模块
目录模式:
* 会在当前目录下寻找`node_modules`文件,找到之后会引入`index.js`。
* 如果没有`index.js`文件,可以通过`package.json`指定入口文件。
第三方模块
网站:https://www.npmjs.com/
通过npm安装第三方包,npm是一个命令,跟随Node.js一起安装。
修改镜像源:npm config set registry https://registry.npm.taobao.org
全局安装资源:
- npm install <package-name> --global
- npm i <package-name> -g
minify ./style.css > ./style.min.css
项目安装:
- 创建项目目录(mkdir project);
- 进入项目目录(cd project);
- 初始化项目(npm init);
- 在项目中安装包;
- npm install <package-name> --sava
- npm i <package-name> -S
./node_modules/bin/minify ./style.css > ./style.main.css
--save/-S : 安装的包,开发和上线都需要
--save-dev/-D : 安装的包,只在开发环境使用
压缩css文件插件
- 安装:
npm i minify -g
- 使用:
minify output.css > output.min.css
将less转为css
- 安装:
npm i less -g
- 使用:
lessc input.less output.css
发布服务
- 安装:
npm i serve -g
- 使用:
serve .
脚手架工具
Yeoman
-
Yeoman是一款脚手架工具
- 可以帮助开发人员创建项目的基础结构代码
-
yo是Yeoman的命令行管理工具
- 可以在命令行运行yeoman的命令
-
生成器:Yeoman中具体的脚手架
- 针对不同项目有不同的额脚手架(例如:网站,APP,小程序等)
Yeoman 使用说明
- 全局安装yo
- npm install --global yo
- 安装generator
- npm install --global generator-webapp
- 通过yo运行generator
- mkdir project-name
- cd project-name
- yo webapp
- 启动应用
- npm run start
自动化构建
npm scripts
-
npm
允许在package.json
文件中,使用scripts
字段定义脚本命令。
npm scripts 自定义脚本命令
- 1.声明命令:在
package.json
文件中,声明scripts
字段定义的脚本命令。"scripts": {"foo": "node bar,js"}
- 2.执行命令:在终端中执行命令
npm run foo
npm scripts 命令的执行方式
-
并行执行:
- 任务1 & 任务2
- 任务之间没有先后顺序,同时执行可以提高执行效率
-
串行执行:
- 任务1 && 任务2
- 任务之间有先后顺序,先执行一个任务,后执行下一个。
注意:并行执行
&
符号在windows中不起作用。-
解决方法:
- 在项目中安装
npm-run-all
插件。npm i npm-run-all -D
. - 并行执行:
npm-run-all -p 脚本1 脚本2 脚本3
- 并行执行简写:
run-p 脚本1 脚本2 脚本3
- 串行执行:
npm-run-all -s 脚本1 脚本2 脚本3
- 串行执行简写:
run-s 脚本1 脚本2 脚本2
- 在项目中安装
示例代码:
// package.json
{
"name": "stylesFile",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
// 现将less文件转换成css文件,然后再压缩。
"style": "lessc styles/index.less styles/index.css && minify styles/index.css > styles/index.min.css"
},
"keywords": [],
"author": "",
"license": "ISC"
}
脚本文件
在ES6中新增加了一些语法,可能在一些浏览器中不支持,所以需要将ES6+的新语法转成ES5的语法。可以使用Babel
插件,将ES6的语法转换成ES5的语法。
- 安装Babel:
npm install -g babel-core babel-cli
- 安装转码规则:
npm install -g babel-preset-env
- 配置转换规则:
.babelrc
一定要将文件放到根目录。不要省略文件名前面的 "."。 - 在npm scripts中添加转换命令(babel src -d dist)
- 执行转换命令
// .babelrc 配置转换规则
{
"presets": [
"env"
]
}
- 转换规则:
babel-preset-env
- 转换命令:
- 单个文件:
babel input.js --out-file output.js
或babel input.js -o oupput.js
- 整个目录:
babel src --out-dir dist
或babel src -d dist
- 单个文件:
代码格式化的校验
- 不同的工程师,代码风格不统一。
- 项目代码提交时,需要保持统一的代码格式。
- 可以通过工具完成代码格式校验。
- 格式校验工具提供编码规范,根据编码规范,自动检查代码。
使用 ESLint,校验js代码。
官网:eslint.org
- 初始化项目(npm init --yes)
- 安装ESLint(npm i eslint -g)
- 初始化配置文件(eslint --init)
- 检查js代码格式。
- 单个文件检查:eslint path/filename.js
- 整个目录检查:eslint path/dirname
初始化配置文件中的rules
表示规则,可以进行配置。
- indent:表示缩进,他的值是一个数组,里面可以配置两个参数。
-
第一个参数:
-
off
:表示关闭。 -
warn
:警告,不影响代码格式检测。 -
error
:错误,检查代码格式
-
-
第二个参数:
- 数字类型,表示缩进。
-
- quotes:表示引号。他的值是一个数组,里面可以配置两个参数。
-
第一个参数:
-
off
:表示关闭。 -
warn
:警告,不影响代码格式检测。 -
error
:错误,检查代码格式
-
-
第二个参数:
- double:表示双引号。
-
StyleLint 校验css代码
用于检测css代码格式规范
官网:stylelint.io
- 初始化项目:npm init --yes
- 安装StyleLint:npm i stylelint -g
- 安装检测标准:npm i stylelint-config-standard
- 创建配置文件:.stylelintrc.json
{"extends": "stylelint-config-standard"}
- 检查CSS代码格式:
- 单个文件:stylelint path/filename.css
- 整个项目:stylelint */.css
自动化构建工具
Gulp
Gulp与npm scripts的区别
Gulp与npm scripts都能够实现自动化构建
-
Gulp语法简单
- Gulp语法就是JavaScript语法。
- npm scripts语法接近shell脚本。
Gulp生态完善,构建效率高。
Gulp使用
- 全局安装gulp客户端:
npm install -g gulp-cli
- 初始化项目:
npm init --yes
- 安装gulp包:
npm install gulp -D
- 新建gulpfile文件,文件必须在项目的跟目录下:
gulpfile.js
- 在gulpfile.js中,创建gulp任务。
- 执行gulp任务:
gulp <task-name>
- 注意:
- gulp任务是一个异步任务,在创建任务的时候,需要创建异步任务,可以添加一个回调函数,在任务中执行回调函数。
- 任务创建成功之后,需要导出任务。
- 示例代码:
const gulp = require("gulp");
// 创建任务
const task1 = (cb) => {
setTimeout(() => {
console.log("task1 is running");
cb();
}, 1000)
};
module.exports = {
task1,
};
Gulp组合任务
Gulp中的组合任务就是指任务并行执行或者串行执行。
并行执行:
- gulp.parallel(task1, task2, task3)
串行执行
- gulp.series(task1, task2, task3)
代码示例:
const gulp = require("gulp");
// 创建任务
const task1 = (cb) => {
setTimeout(() => {
console.log("task1 is running");
cb();
}, 1000);
};
const task2 = (cb) => {
setTimeout(() => {
console.log("task2 is running");
cb();
}, 1000);
};
const task3 = (cb) => {
setTimeout(() => {
console.log("task3 is running");
cb();
}, 1000);
};
// 并行执行
exports.p = gulp.parallel(task1, task2, task3);
// 串行执行
exports.s = gulp.series(task1, task2, task3);
// 终端执行命令方式:
// gulp p
// gulp s
gulp 文件操作
Gulp的文件操作是基于流的构建系统来操作文件的。
步骤:
- 输入(读取流)
- 对应函数:src(),读取文件。
- 参数1:读取的文件地址。
- 参数2:写入文件的参照路径,对象格式。
- {base: "src"} 表示参照src中的文件路径最终写入文件。
- 加工(转换流)
- 对应函数:pipe()
- 管道可以进行多次操作,称作为管道流
- 输出(写入流)
- 对应函数:dest()
- 参数:将文件写入的目标路径。
示例代码:
// 文件操作任务,本身就是异步任务,所以不需要回调函数
const fileStyle = () => {
return gulp
.src("src/style/main.less", { base: "src" })
.pipe(gulp.dest("dest"));
};
module.exports = {
fileStyle,
};
使用gulp插件完成文件转换
// 文件转换任务
// gulp-less 将less文件转换成css文件
const gulpLess = require("gulp-less");
// gulp-clean-css 压缩css文件
const cleanCss = require("gulp-clean-css");
// gulp-rename 文件重命名
const rename = require("gulp-rename");
const fileStyle = () => {
return gulp
.src("src/style/main.less", { base: "src" })
.pipe(gulpLess())
.pipe(cleanCss())
.pipe(rename({ extname: ".min.css" }))
.pipe(gulp.dest("dest"));
};
module.exports = {
fileStyle,
};
CSS hack
- css代码也会存在兼容性问题。同一段css代码,在不同的浏览器上的呈现效果不同。
- 针对不同浏览器写相对应的css代码的过程,叫做 CSS hack。
- CSS hack的目的就是使css代码兼容不同的浏览器。
CSS hack属性前缀法
- 给css属性添加浏览器特有的前缀。称之为属性前缀法。
浏览器 | 前缀 |
---|---|
IE | -ms- |
chrome | -webkit- |
Safari | -webkit- |
Firefox | -moz- |
Opera | -o- |
Autoprefixer
- Autoprefixer插件可以自动的将一些css的属性,添加前缀,不用自己手动书写属性的前缀名称。
- Autoprefixer使用caniuse.com的数据,来决定哪些属性需要加前缀。caniuse.com可以查询属性的兼容性。
安装:npm i gulp-autoprefixer
示例代码:
const gulp = require("gulp");
// gulp-less 将less文件转换成css文件
const gulpLess = require("gulp-less");
// gulp-clean-css 压缩css文件
const cleanCss = require("gulp-clean-css");
// gulp-rename 文件重命名
const rename = require("gulp-rename");
// gulp-autoprefixer css自动添加前缀
const autoprefixer = require("gulp-autoprefixer");
const fileStyle = () => {
return (
gulp
.src("src/style/main.less", { base: "src" })
.pipe(gulpLess())
.pipe(autoprefixer())
// .pipe(cleanCss())
// .pipe(rename({ extname: ".min.css" }))
.pipe(gulp.dest("dest"))
);
};
module.exports = {
fileStyle,
};
Gulp 脚本文件转换
Gulp 构建脚本文件所需插件
-
gulp-babel:将ES6新语法转换成ES5语法。
- npm install --save-dev gulp-babel@7 babel-core babel-preset-env
-
gulp-uglify:压缩js代码。
- npm i gulp-uglify -D
gulp-rename:对文件进行重命名。
示例代码:
const gulp = require("gulp");
// gulp-babel 将ES6新语法转换成ES5
const gulpBabel = require("gulp-babel");
// gulp-uglify 压缩js代码
const gulpUglify = require("gulp-uglify");
// gulp-rename 重命名
const rename = require("gulp-rename");
// 创建脚本文件转换任务
const scripts = () => {
return gulp
.src("src/js/main.js")
.pipe(gulpBabel({ presets: "babel-preset-env" }))
.pipe(gulpUglify())
.pipe(rename({ extname: ".min.js" }))
.pipe(gulp.dest("dest/script"));
};
module.exports = {
scripts,
};
Gulp 构建HTML文件
Gulp HTML文件所需要的插件
- gulp-htmlmin:压缩html文件。
示例代码:
// 创建压缩html文件任务
const html = () => {
return gulp
.src("src/index.html")
.pipe(
htmlmin({
collapseWhitespace: true,
minifyCSS: true,
minifyJS: true,
})
)
.pipe(gulp.dest("dest"));
};
Gulp 构建图片文件
图片文件所需插件
- gulp-imagemin:压缩图片文件
Gulp 文件清除
文件清除所需插件
- del 删除文件和目录
const clearFile = () => {
return del("dist");
};
Gulp 发布服务
通过插件将html,css,js等文件压缩之后,最终要发布的是压缩之后的文件。
开发服务器插件
- browser-sync:服务发布
const server = () => {
watch("src/index.html", fileHtml);
watch("src/css/*.css", fileStyle);
watch("src/js/*.js", fileScript);
browserSync.init({
notify: false,
files: "dist/**", // 监听dist下的所有文件
server: {
baseDir: "./dist", // 指定服务启动的目录
routes: { "/node_modules": "node_modules", "/lib": "lib" },
},
});
};
Gulp 中使用Bootstrap
- 安装:
- bootstrap:提供常用的页面效果
- jquery:Bootstrap的依赖包