第三方模块 Gulp
基于node平台开发的前端构建工具
将机械化操作编写成任务,想要执行机械化操作时执行一个命令行命令任务就能自动执行了。
用机器代替手工,提高开发效率。比如压缩css代码。
Gulp能做什么
- 项目上线,HTML、CSS、JS文件压缩合并
- 语法转换(es6、less…)
- 公共文件抽离
- 修改文件浏览器自动刷新
Gulp使用
- 项目根目录下,使用npm install gulp下载gulp库文件
- 在项目根目录下建立gulpfile.js文件
- 重构项目的文件夹结构,src目录放置源代码文件,dist目录放置构建后文件
- 在gulpfile.js文件中编写任务.
- 在命令行工具中执行gulp任务
gulp 方法名
还有一个命令行工具需要安装,在后续使用的时候会用(全局安装),有时已经安装
npm install gulp-cli -g
Gulp的提供的方法:
gulp.src(): 获取任务要处理的文件
gulp.dest(): 输出文件
gulp.task(): 建立gulp任务
gulp.watch(): 监控文件的变化
示例:
const gulp = require('gulp');
//使用gulp.task()方法建立任务(这是一个拷贝操作)
gulp.task('first', () => {
//获取要处理的文件
gulp.src('./src/css/base.css')
//将处理后的文件输出到dist目录
.pipe(gulp.dest('./dist/css'));
});
//注意,处理的代码必须写在 .pipe(); 中。
gulp.task('任务名',回调函数):
gulp插件
gulp提供方法非常少,很多功能都是由插件完成的。
常用插件:
- gulp-htmlmin:html文件压缩
- gulp-csso:压缩css
- gulp-babel:JavaScript语法转化
- gulp-less:less语法转化
- gulp-uglify:压缩混淆JavaScript
- gulp-file-include公共文件包含
- browsersync浏览器实时同步
插件的使用:
- 下载插件
- 在gulpfile.js中引入这个插件
- 调用插件
- 在cmd中执行
gulp 方法名
⭐⭐⭐插件很多,在使用的时候去文档里看引入和使用方法就可以。
html任务:
const gulp = require('gulp');
const htmlmin = require('gulp-htmlmin');
gulp.task('htmlmin', () => {
gulp.src('./src/*.html')
.pipe(htmlmin({ collapseWhitespace: true })) //是否压缩
console.log('压缩完成');
.pipe(gulp.dest('./dist'));
console.log('输出完成');
});
注意,任务是有顺序的,先抽取,后压缩,后输出。注意执行顺序不能错。
把公共部分拿出来,新建文件放进去(只放进去就可以,不需要加html>head+body),然后在原地留下 @@include('./commen/header.html')
,这个语法是由gulp-file-include提供。
const gulp = require('gulp');
const htmlmin = require('gulp-htmlmin');
const fileinclude = require('gulp-file-include');
gulp.task('htmlmin', () => { //建立任务
gulp.src('./src/*.html') //获取文件
.pipe(fileinclude()) //抽离公共部分
.pipe(htmlmin({ collapseWhitespace: true })) //是否压缩
.pipe(gulp.dest('./dist')); //输出文件
});
css任务:
const less = require('gulp-less');
gulp.task('cssmin',()=>{
gulp.src('./src/less/*.less')
.pipe(less())
.pipe(gulp.dest('./dist/css/'));
});
多个路径同时操作(用数组);
gulp.src(['./src/less/*.less','./src/css/*.css'])
gulp.task('cssmin',()=>{
gulp.src('./src/css/*.css')
.pipe(csso())
.pipe(gulp.dest('./dist/css/'));
});
js任务:
gulp.task('jsmin', () => {
gulp.src('./src/js/*.js')
.pipe(babel({
//判断当前代码的运行环境,将代码转换成当前代码环境支持的代码
presets: ['@babel/env']
}))
.pipe(gulp.dest('./dist/js/'));
});
gulp.task('jsmin', () => {
gulp.src('./src/js/*.js')
.pipe(babel({
//判断当前代码的运行环境,将代码转换成当前代码环境支持的代码
presets: ['@babel/env']
}))
.pipe(uglify())
.pipe(gulp.dest('./dist/js/'));
});
复制文件夹
gulp.task('copy',()=>{
//复制img
gulp.src('./src/img/*')
.pipe(gulp.dest('./dist/img/'));
//复制fonts
gulp.src('./src/fonts/*')
.pipe(gulp.dest('./dist/fonts/'));
//复制upload
gulp.src('./src/upload/*')
.pipe(gulp.dest('./dist/upload/'));
});
执行一个任务,其他任务跟着一起执行。
gulp.task('default',['htmlmin','cssmin','jsmin','copy']);
node_modules文件夹的问题
存放Gulp插件的文件夹,内部文件非常多。
- 文件夹以及文件过多过碎,当我们将项目整体拷贝给别人的时候,传输速度会很慢很慢.
- 复杂的模块依赖关系需要被记录,确保模块的版本和当前保持一致,否则会导致当前项目运行报错
1的解决方法
⭐⭐⭐在项目拷贝的时候,不需要传输这个文件夹,在package.json文件中储存所有的插件及其版本信息。
项目描述文件,记录了当前项目信息,例如项目名称、版本、作者、github地址、当前项目依赖了哪些第三方模块等。使用npm init -y
命令生成。
npm可以同时下载多个插件,空格隔开。npm install gulp gulp-csso
协同办公时,怎么同步插件环境,给package.json就可以了。
拿到package.json文件后,放到项目目录下,打开cmd,执行npm install
,会自动找到package.json文件中保存的所有插件及其版本信息,然后全部下载。
2的解决方法
项目依赖
在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖
使用npm install 包名命令下载的文件会默认被添加到package,json文件的dependencies字段中
开发依赖
在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖
使用npm install 包名–save-dev命令将包添加到package.json文件的devDependencies字段中
安装开发依赖
npm install gulp --save-dev
区分项目依赖和开发依赖的好处,在不同环境下安装不同的依赖。
开发环境,使用npm install
下载全部依赖
运行环境(服务器环境),使用npm install --production
下载项目依赖
在安装第三方模块的时候,会生成一个文件package-lock.json。作用:
- 锁定包的版本,确保再次下载时不会因为包版本不同而产生问题
- 加快下载速度,因为该文件中已经记录了项目所依赖第三方包的树状结构和包的下载地址,重新安装时只需下载即可,不需要做额外的工作
package.json文件夹中有个scripts,可以使用命令别名,把较长的命令使用别名代替。
Node.js中模块的加载机制
模块查找规则
一、当模块拥有路径但没有后缀时
require(./find.js');
- require方法根据模块路径查找模块,如果是完整路径,直接引入模块。
require(./find');
- 如果模块后缀省略,先找同名JS文件,找不到再找同名JS文件夹
- 如果找到了同名文件夹,找文件夹中的index.js
- 如果文件夹中没有index.js就会去当前文件夹中的package.js文件中查找main选项中的入口文件
- 如果找指定的入口文件不存在或者没有指定入口文件就会报错,模块没有被找到,就会报错。
二、当模块没有路径且没有后缀时
require('find');
- Node.js会假设它是系统模块
- Node.js会去node_modules文件夹中
- 首先看是否有该名字的JS文件
- 再看是否有该名字的文件夹
- 如果是文件夹看里面是否有index.js
- 如果没有index.js查看该文件夹中的package.json中的main选项确定模块入口文件
- 否则找不到报错
URL
统一资源定位符,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式,我们平时所说的网页地址指的即是URL。
URL的组成:
传输协议 😕/ 服务器IP或域名 : 端口 / 资源所在位置标识
http://www.baidu.cn/img/index.html
http:超文本传输协议,提供了一种发布和接收HTML页面的方法
端口默认80
开发过程中客户端和服务器端说明
在开发阶段,客户端和服务器端使用同一台电脑,即开发人员电脑。
开发人员电脑:客户端(浏览器)、服务器端(Node)
本机域名:localhost
本地IP:127.0.0.1
创建web服务器
创建server文件夹,添加app.js文件,代码写入其中,而后去cmd中用node命令执行。
//创建网站服务器
//引入系统模块
const http = require('http');
//创建网站服务器
const app = http.createServer();
//当客户端发送请求的时候
app.on('request', (req, res) => {
//响应
res.end('<h1>nihao</h1>');
});
//监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000。');
http协议
超文本传输协议。
超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准。
报文
在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式。
分为 请求报文 和 响应报文。
查看请求的方法:
浏览器 F12 — Network ,F5刷新就能看到请求了,这时候点击任一请求,会看到:
请求报文
- 请求方式:
get:获取数据使用,
post:添加数据使用,一般逻辑也用post,post较为安全
//根据请求方式,做出不同的回应
const http = require('http');
const app = http.createServer();
app.on('request', (req, res) => {
//获取请求方式 req.method
if (req.method == 'POST') { res.end('<h1>POST</h1>'); }
else if (req.method == 'GET'){ res.end('<h1>GET</h1>'); }
res.end('<h1>nihao</h1>');
});
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000。');
//请求模块
<form action="http://127.0.0.1:3000" method="POST">
<input type="text" name="" id="">
<input type="submit" value="提交">
</form>
在这个时候,客户端请求http://127.0.0.1:3000/xxxx
,后跟的xxx不管是什么,都是get请求。
那我们怎么才能根据不同的请求地址,返回不同的页面呢。
- 请求地址
解决这个问题呢,我们可以用到请求地址。
例如我们访问:http://127.0.0.1:3000/index
console.log(req.url); //返回值: /index
那么,我们就可以通过判断req.url的返回值来进行不同的响应。
const http = require('http');
const app = http.createServer();
app.on('request', (req, res) => {
//响应
//获取请求地址
// console.log(req.url);
if(req.url == '/index'){
res.end('<h1>index</h1>');
}
else if(req.url == '/list'){
res.end('<h1>list</h1>');
}else{
res.end('<h1>no document</h1>');
}
});
//监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000。');
然而,当我们不给任何参数的时候,获取到的是一个 /
,那么再加上一个 /
,就可以了。
//获取请求地址
// console.log(req.url);
if(req.url == '/index' || req.url == '/'){
res.end('<h1>index</h1>');
}
else if(req.url == '/list'){
res.end('<h1>list</h1>');
}else{
res.end('<h1>no document</h1>');
}
我们如何获取请求报文里的东西呢: 上图F12中的Request Headers
使用 req.headers
//获取请求报文里的东西
console.log(req.headers);
获取其中的某一个呢:
//获取请求报文里某一个东西
console.log(req.headers['cookie']);
响应报文
- http状态码
以状态码的形式返回,告诉客户端情况
200 请求成功
404 请求的资源没有被找到
500 服务器端错误
400 客户端请求有语法错误
为服务器端设置不同的响应,使用 res ,res.writeHead
res.writeHead(400); //书写响应头。第一个参数不写默认200,
http状态码就是一个标识,返回的时候状态码设置成多少,完全取决于你当前的情况。
- 内容类型
返回什么文件要加上文件类型。
//部分文件类型
text/html
text/css
application/javascript
image/jpeg
application/json
text/plain
res.writeHead(200, { //书写响应头。第一个参数不写默认200,
'content-type': 'text/html'
});
我们上面输出的文字都是英文,因为输出中文会出错,我们现在解决这个出错的问题,给它加上utf-8:
const http = require('http');
const app = http.createServer();
app.on('request', (req, res) => {
//响应
res.writeHead(200, { //书写响应头。第一个参数不写默认200,
'content-type': 'text/html;charset=utf8;'
});
//获取请求地址
// console.log(req.url);
if(req.url == '/index' || req.url == '/'){
res.end('<h1>index首页</h1>');
}
else if(req.url == '/list'){
res.end('<h1>list分类</h1>');
}else{
res.end('<h1>no document没找到</h1>');
}
});
//监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000。');
http请求与响应处理
- 请求参数:
客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作。
- GET请求参数
参数被放置在浏览器地址栏中,例如:http://localhost:3000/?name=zhangsan&age=20
?后加参数,键值对用=连接,&连接多个参数。
案例:
请求地址:http://localhost:3000/?name=zhangsan&age=20
console.log(req.url); // /index?name=xy&age=12
我们不需要对参数进行截取,node.js中提供了内置url模块,用于处理url地址。
引入url模块:
const url =require('url');
url.parse
有两个参数,要解析的url地址 和 将查询解析成对象形式
写第一个参数,写入要解析的url地址:
console.log(url.parse(req.url));
写第二个参数,将查询解析成对象形式
console.log(url.parse(req.url,true));
获取解析出来的对象中的参数:
let params = url.parse(req.url,true).query;
console.log(params);
总结也就是,使用url模块将返回的字符串转换成对象。
上面我们对req.url
进行判断,以显示页面,那么我们加上参数之后,就全部判断无效了,返回了找不到页面,上图我们看到url.parse(req.url)
返回的有一个pathname
是地址,这里我们可以判断url.pathname
。
const http = require('http');
const url =require('url');
const app = http.createServer();
app.on('request', (req, res) => {
//响应
let {query , pathname} = url.parse(req.url,true); // ES6对象解构
console.log(query.name);
console.log(query.age);
//获取请求地址
if(pathname == '/index' || pathname == '/'){
res.end('<h1>index首页</h1>');
}
else if(pathname == '/list'){
res.end('<h1>list分类</h1>');
}else{
res.end('<h1>no document没找到</h1>');
}
});
//监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000。');
- POST请求参数
请求以name获取到,所以那么一定要写。
发送请求的时候,在Network中有传输的form内容。
由于post请求的参数没有在地址栏之中,所以不能用req.url
获取。
理论上post参数是可以无限多的,所以当数量多的时候会分批次获取。
post参数是通过事件的舱室接收的。
data 、 end
开始请求参数传递的时候触发data,传递完成触发end。
const http = require('http');
const app = http.createServer();
app.on('request', (req, res) => {
//由于参数不是一次传递完成的,所以我们要拼接
let postParams = '';
req.on('data',params=>{
postParams += params;
});
req.on('end',()=>{
console.log(postParams); // username=123&userpwd=qwe 注意这里还是字符串
});
res.end('ok'); //对于客户端的每次请求,服务器端都要给出响应,否则客户端一直处于等待状态。
});
//监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000。');
注意这里的返回值还是字符串格式。
注意url模块是处理请求地址的,这里是字符串,不能使用url模块。
node提供了另一个模块,querystring
模块,用来处理POST数据。
const http = require('http');
//处理请求参数模块
const querystring = require('querystring');
const app = http.createServer();
app.on('request', (req, res) => {
//由于参数不是一次传递完成的,所以我们要拼接
let postParams = '';
req.on('data', params => {
postParams += params;
});
req.on('end', () => {
let querys = querystring.parse(postParams);
console.log(querys);
});
res.end('ok'); //对于客户端的每次请求,服务器端都要给出响应,否则客户端一直处于等待状态。
});
//监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000。');
post请求总结:
- 参数被放置在请求体中进行传输
- 获取POST参数需要使用data事件和end事件
- 使用querystring系统模块将参数转换为对象格式
路由
路由是指客户端请求地址与服务器端程序代码的对应关系。简单的说,就是请求什么响应什么。
实际上路由就是一堆判断代码,判断请求什么,决定响应什么。
路由代码的实现:
const http = require('http');
const url = require('url');
const app = http.createServer();
app.on('request', (req, res) => {
const method = req.method.toLowerCase();
const pathname = url.parse(req.url).pathname;
res.writeHead(200, {
'content-type': 'text/html;charset=utf8'
});
if (method == 'get') {
if (pathname == '/index' || pathname == '/') {
res.end('<h1>index首页</h1>');
}
else if (pathname == '/list') {
res.end('<h1>list分类</h1>');
} else {
res.end('<h1>no document没找到</h1>');
}
} else if (method == 'post') {
}
});
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000');
静态资源访问
服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、image文件。
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const app = http.createServer();
app.on('request', (req, res) => {
//获取用户路径,过滤参数
let pathname = url.parse(req.url).pathname;
//将用户的请求路径转换为实际的服务器硬盘路径
let realPath =path.join(__dirname , 'public' + pathname);
// res.end(realPath);
fs.readFile(realPath,(error,result)=>{
if(error!=null){
res.writeHead(404, {
'content-type': 'text/html;charset=utf8'
});
res.end('文件读取失败!');
}
res.end(result);
});
});
//监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000');
在这里,不写地址的时候,是无法读取到index的;
这里我们要给用户返回文件类型,
我们接下来完善一下。
动态资源
相同的请求地址不同的响应资源,这种资源就是动态资源。
http://baidu.com/search?id=1
http://baidu.com/search?id=2
这两个可能就不一样。
gulp 20.此部分为服务器端内容,稍后补上。