前端工程化-npm-gulp-webpack
题目1: 如何全局安装一个 node 应用?
Node
模块采用npm install
命令安装。
每个模块可以“全局安装”,也可以“本地安装”。“全局安装”指的是将一个模块安装到系统目录中,各个项目都可以调用。一般来说,全局安装只适用于工具模块,比如eslint
和gulp
。“本地安装”指的是将一个模块下载到当前项目的node_modules
子目录,然后只有在项目目录之中,才能调用这个模块。
# 本地安装
npm install <package name>
# 全局安装
npm install -g <package name>
# npm更新最新版本
cnpm install npm@lastest -g
如果下载速度比较慢,可以使用淘宝的镜像
//先执行下面命令
npm install -g cnpm --registry=https://registry.npm.taobao.org
//以后安装就用 cnpm 代替 npm
cnpm install -g xxx
install 可以缩写成 i
题目2: package.json 有什么作用?
命令行npm init
可以初始化生成一个package.json
package.json
是一个 json
格式的文件,用来记录当前的npm
包的相关信息,如
name
:包的名字
version
:版本号
description
:描述
main
:包的入口文件
script
: 运行脚本命令的npm命令行缩写
author
: 作者
license
: 版权信息
dependencies
:项目运行依赖,发布的时候,不需要发布依赖的包,只要发布其名字,别人下载的时候,会自动下载依赖的包。
devDependencies
:开发依赖,只有自己本地开发时候用的依赖包,发布以后别人不能用。
{
"name": "packageName",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "cg",
"license": "ISC",
"dependencies": {},
"devDependencies": {}
}
题目3: npm install --save app 与 npm install --save-dev app有什么区别?
命令行参数
当你为你的模块安装一个依赖模块时,正常情况下你得先安装他们,在模块根目录下npm install module-name
,然后连同版本号手动将他们添加到模块配置文件package.json
中的依赖里(dependencies)
。
-save
和save-dev
可以省掉你手动修改package.json
文件的步骤。
npm install module-name -save
自动把模块和版本号添加到dependencies
部分。
npm install module-name -save-dev
自动把模块和版本号添加到devdependencies
部分。
配置文件
package.json
提供了三种依赖关系定义:
- dependencies
- peerDependencies
- devDependencies
devDependencies
是开发时依赖,比如你模块用了mocha
测试框架,那么你的模块的开发就依赖mocha
,如果别人想为你的模块贡献代码,他就需要安装mocha
。但是只是使用你的模块的人,就不需要mocha
。
peerDependencies
是为插件准备的。比如grunt
的插件,里面没有require(“grunt”)
,所以用dependencies
会有问题。所以需要单独列出。
题目4: node_modules的查找路径是怎样的?
- 先从当前目录下查找node_modules文件夹,如果没有,则到父级文件夹查找node_modules文件夹,直至查找到根目录。
题目5: npm3与 npm2相比有什么改进?yarn和 npm 相比有什么优势? (选做题目)
模块和包
很多时候我们并不是很区分模块和包,因为我们一般使用场景就是 npm install xxx
,然后在文件里直接require('xxx')
。
但是考虑到很多时候我们也可以随意 require
一个本地自己写的 JS 文件,只要其按照CommonJS
规范 export
即可,因此这里需要严格区分下:
- 模块:符合
CommonJS
规范的文件 - 包:一个包含
package.json
以及入口文件的文件夹
这里也不是绝对这样定义的,不过最终表现出来的就是这些,而 npm
的所有的管理的对象都必须包含 package.json
文件,用于模块确定依赖关系。
说了这么多,只是为了强调:npm
是包管理器。
npm2 问题
npm2 安装依赖的时候比较简单直接,直接按照包依赖的树形结构下载填充本地目录结构。
因为 npm 设计的初衷就是考虑到了包依赖的版本错综复杂的关系,同一个包因为被依赖的关系原因会出现多个版本,简单地填充结构保证了无论是安装还是删除都会有统一的行为和结构。
比如一个 App 里模块 A 和 C 都依赖 B,无论被依赖的 B 是否是同一个版本,都会生成对应结构:
于是缺陷就凸显出来了,太深的目录树结构会严重影响效率,甚至在 Windows 下可能会超出系统路径限制的长度。另外,在 Windows 有删 node_modules 目录经历的可能都经历过漫长的等待。
npm3 解决方式
针对 npm2
的问题,npm3
加了点算法,直白的解释就是:npm install
时会按照 package.json
里依赖的顺序依次解析,遇到新的包就把它放在第一级目录,后面如果遇到一级目录已经存在的包,会先判断版本,如果版本一样则忽略,否则会按照npm2
的方式依次挂在依赖包目录下
还是刚刚的栗子,可以看下npm2
和 npm3
生成的结构对比:
试想,在包版本差异化不太严重的情况下,这种构建方式会几乎把所有包放在一级目录下,很大程度上提升了效率以及节省了部分磁盘空间。
其实,npm3 这种方式在理论上其实会趋于一种平稳的状态,因为你可能会说,
npm3
在极端情况下也可能退化为 npm2
的行为,不过这种情况在一般情况下是可以忽略的。npm3
还有个优点,就是在动态安装更新包的时候,是可以进一步调整目录结构的,比如某种依赖已经如下:具体依赖细节我们不用追究,假设 E_v1.0 模块是依赖 B_v1.0 的,此时我们更新 E 到 v2.0,假设此时依赖 B_v2.0 了,那么最终生成的结构会是如下:
是不是觉得很多冗余?其实只需执行下
npm dedupe
就会变成如下结构:
这已经很接近我们理想的使用场景了!
npm3 新的问题
你以为就这么完了吗?注意到上面提到的npm3
会按照package.json
的顺序解析目录树,试着看下下面的场景:
对应的
dependencies
为:
"dependencies": { "mod-a": "^1.0.0", "mod-c": "^1.0.0", "mod-d": "^1.0.0", "mod-e": "^1.0.0"}
如果恰好 A_v1.0 依赖 B_v1.0,然后我们本地升级了 A 到 v2.0,假设此时依赖 B_v2.0,那么此时目录结构会变成:
而此时部署到测试平台呢?因为 mod-a 在第一个,所以会优先解析,也就是 B_v2.0 会优先占据一级目录,最终可能目录结构为:
开发环境和测试环境的
node_modules
目录结构不一样了!!这个问题很可能会导致一些很微妙的问题,而且很难调试。如何解决呢?就是本地每次安装或者升级包后,完整删除node_modules
目录然后再install
一次……(感觉比npm2
还粗暴)
新的工具 yarn
除了上面的问题,还有个严重的问题。npm
在使用的时候大多是用语义化版本号管理包依赖的,比如 ~1.0.0
表示只更新补丁,但是世界辣么大,什么人没有?说不定哪个开发者就在 patch version
上就搞了major
的升级,即使你本地使用固定版本号也无济于事。
当然,后面 npm
也有 shrinkwrap 机制来保证这种一致性,不过说实话 npm-shrinkwrap.json
略难看,基本属于给 npm
打补丁,让我在一个项目引入这个几乎无法 review
的文件肯定会不开心的。
太多因素导致了 npm
已经步履维艰了,估计 Facebook 也累了吧,于是前不久搞了yarn
用来替代npm
了。
我觉得yarn
革命性的更改在于其改变了构建的步骤,其它有点都是新构建方式的副产物,yarn
构建步骤如下:
- Resolution: 向仓库请求依赖关系
- Fetching: 看看本地缓存了没有,否则把包拉到缓存里
- Linking: 直接全部从缓存里构建好目录树放到 node_modules 里
这里的缓存机制很像 mvn
之类的,而且其还引入了lockfile
用于锁定版本号,这很类似shrinkwrap
,不过格式比npm-shrinkwrap.json
更好 review
。除了这些特别明显的改进,还有很多体验上的提升,具体可以看官方博客。
题目6: webpack是什么?和其他同类型工具比有什么优势?
WebPack
可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript
模块以及其它的一些浏览器不能直接运行的拓展语言(Scss
,TypeScript
等),并将其打包为合适的格式以供浏览器使用。
WebPack和Grunt以及Gulp相比有什么特性
其实 Webpack 和另外两个并没有太多的可比性,Gulp/Grunt 是一种能够优化前端的开发流程的工具,而WebPack 是一种模块化的解决方案,不过Webpack 的优点使得 Webpack 可以替代 Gulp/Grunt 类的工具。
Grunt 和 Gulp 的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,这个工具之后可以自动替你完成这些任务。
** Grunt 和 Gulp** 的工作流程
Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。
如果实在要把二者进行比较,Webpack的处理速度更快更直接,能打包更多不同类型的文件。
题目7:npm script是什么?如何使用?
npm
允许在package.json
文件里面,使用scripts
字段定义脚本命令。
{
// ...
"scripts": {
"build": "node build.js"
}
}
上面代码是package.json
文件的一个片段,里面的scripts
字段是一个对象。它的每一个属性,对应一段脚本。比如,build
命令对应的脚本是node build.js
。
命令行下使用npm run
命令,就可以执行这段脚本。
$ npm run build
等同于执行
$ node build.js
这些定义在package.json
里面的脚本,就称为 npm
脚本。它的优点很多。
- 项目的相关脚本,可以集中在一个地方。
- 不同项目的脚本命令,只要功能相同,就可以有同样的对外接口。用户不需要知道怎么测试你的项目,只要运行npm run test即可。
- 可以利用 npm 提供的很多辅助功能。
查看当前项目的所有 npm 脚本命令,可以使用不带任何参数的npm run命令。
$ npm run
题目8: 使用 webpack 替换 入门-任务15中模块化使用的 requriejs
github代码
题目9:gulp是什么?使用 gulp 实现图片压缩、CSS 压缩合并、JS 压缩合并
简介:
gulp 是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器;她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;使用她,我们不仅可以很愉快的编写代码,而且大大提高我们的工作效率。
gulp中文网
入门指南
- 全局安装 gulp:
$ npm install --global gulp
- 作为项目的开发依赖(devDependencies)安装:
$ npm install --save-dev gulp
- 在项目根目录下创建一个名为 gulpfile.js 的文件:
var gulp = require('gulp');
gulp.task('default', function() {
// 将你的默认的任务代码放在这
});
- 运行 gulp:
$ gulp
默认的名为 default 的任务(task)将会被运行,在这里,这个任务并未做任何事情。
想要单独执行特定的任务(task),请输入 gulp <task> <othertask>。
gulp的常用插件
var gulp=require('gulp');
var del=require('del'); // 删除文件
var minify=require('gulp-minify-css'); //压缩CSS
var uglify=require('gulp-uglify'); // 压缩JS
var concat=require('gulp-concat'); // 合并文件
var imagemin=require('gulp-imagemin'); // 缩小图片
gulp.task('css', function() {
gulp.src('css/*.css')
.pipe(concat('merge.css'))
.pipe(minify())
.pipe(gulp.dest('dist2/css'));
})
gulp.task('js', function() {
gulp.src('js/**/*.js')
.pipe(concat('merge.js'))
.pipe(uglify())
.pipe(gulp.dest('dist2/js'));
})
gulp.task('pic', function() {
gulp.src('images/*')
.pipe(imagemin())
.pipe(gulp.dest('dist2/images'));
})
gulp.task('clean', function() {
del([
'dist2'
])
})
gulp.task('default',['css','js','pic'])