前端工程化

前端工程化

  • 前端工程化就是通过各种工具和拘束,提升前端开发效率的过程。

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/

内置模块

文档地址:http://nodejs.cn/api/

内置模块-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>
      如果文件不存在,则创建文件
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

https://www.yeoman.io

  • 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.jsbabel input.js -o oupput.js
    • 整个目录:babel src --out-dir distbabel 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的依赖包
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容