使用工具前,必须要理解为什么需要这些工具,这些工具解决了什么问题。本文,先带大家看下前端发展主流方向,然后基于此介绍常见工具的作用和使用。
前端发展主流方向
前后端分离
现在前端小年轻一提就是前后端分离,到底什么是前后端分离,为什么需要前后端分离。深入理解这个,会发现现代前端也是一步步演变过来的。
服务端负责分离
经历过html 4.0/jquery/php时代的程序员应该清楚,如下,最开始的想法很朴素:前端工程师负责前端页面html+js+css的编写,然后把需要填充的页面位置换成模板语法(比如文字/图片等);后端工程师负责生成对应的填充元素对应的值,调用模板引擎渲染替换对应模板中的值,生成的网页返回给前端展示;核心就是服务端按照模板替换真实的值,分工明确而且相对可控,各司其职。
这时候常见的模板引擎比如php的smarty,java的FreeMarker,C#的razor等。那时候php很流行,因为非常匹配这种用法。比如,一个典型的smarty模板如下
<!DOCTYPE html>
<html>
<head>
<meta charset="gb2312">
<title><{$title}></title>
</head>
<body>
你好,<{$name}>
</body>
</html>
php中调用渲染代码,如下
<?php
require './Smarty/smarty.class.php';
$smarty=new smarty();
...
//给变量赋值
$smarty->assign('title','demo');
$smarty->assign('name','tom');
//渲染模板文件返回
$smarty->display('demo.html');
但这样也存在明显的缺点:
1.无法实现前端的灵活控制,灵活性取决于服务端和模板引擎,无法实现复杂应用
2.每次都返回html,流量消耗大且交互体验差
当然现在这种方式在很多地方也有应用,特别不是很复杂的应用和后台管理类,仍然有用武之地
前端负责分离
实际上前端分离的技术底座ajax出现很早,最开始大家只是用它做表单异步验证,jQuery然后到Angular的出现推动了完全前端分离的思想,如下,也就是整个页面展示交互的逻辑完全由前端控制,后端只是负责提供数据和后台逻辑,双方职责彻底划分开。
当前前端负责分离是主流,基于此,react/vue等开始把mvvm 组件化等概念进一步推进,前端彻底拥有自主权才出现现在的前端大爆发,各种复杂前端应用才成为可能。
模块化和组件化
模块化和组件化也是现代化前端常提的概念。
模块化(modules)
前面讲到模块化是现代前端的一个特点,为什么需要模块?
对比其他语言,就可以看到模块的重要性了,传统的前端依赖script标签引入js,大多数是一锅烩,一个超大文件写满js,也不考虑复用性。模块化的诞生,让前端得以像大多数后端语言一样使用,比如CommonJS规范如下
exports和require即可。
// 定义模块math.js
var total = 10;
function add(a, b) {
return a + b;
}
// 需要向外暴露的函数、变量
module.exports = {
add: add,
total: total
}
/** 必须加./路径,不加的话只会去node_modules文件找 **/
// 引用自定义的模块时,参数包含路径,可省略.js
var math = require('./math');
math.add(2, 5);
// 引用核心模块时,不需要带路径
var http = require('http');
http.createService(...).listen(3000);
模块化带来的好处是
1.标准化,大功能可以拆成很多小功能,按照标准独立实现,适合团队合作,也能快速整合
2.复用性,大量标准库基于此实现,最典型是nodejs的CommonJS规范推动了nodejs扩展库的发展
基于模块化,复杂前端应用的管理才得以实现。前面提到模块化规范,发展进程上出现很多模块化规范,可以参考这篇文章《前端模块化——彻底搞懂AMD、CMD、UMD、ESM和CommonJS》。对于我们普通开发着来说目前事实上的规范就是nodejs的CommonJS和ES6的标准module,学会遵守规范使用即可。
组件化(components)
为什么需要组件化,这个也有类比,看桌面编程,特别是做过VB编程的,应该知道每个按钮/每个文本框都有自己的样式/方法/可以独立定义点击响应和行为,甚至可以实现自定义的COM组件;再看后端编程,特别是微服务相关,各司其职,负责路由/配置/认证的都是单独的微服务(组件)。
严格来说组件化就是业务模块化。意思就是组件相当于按照业务需求拆分的更精细的模块。参考《深入探讨前端组件化开发 》,按照刚才的类比可以看到,包括两方面
1.功能的组件
负责独立的功能,比如vue中的路由组件 vue-router/状态存储组件vuex等
2.界面的组件
参考 《搞懂什么是前端“组件化”开发》,传统开发中,项目的目录结构一般是把HTML文件放在最外层,把CSS文件、JavaScript文件放在单独的目录中,如下图所示:
这样一整个html声明结构,包括一大块css声明样式,还有一大块js描述逻辑,以整体为中心,这样的代价是每个相同的逻辑块(比如按钮/文本/页脚)都是独有的,别的项目要用只能拷贝不能重建。
组件化的目的就是每个组件都有自己的结构、样式和代码。 参考如下vue工程,每块当作一个组件,每个组件都可封装在一个vue文件中,独立使用html/css/js描述,整个页面的编写就是各个组件的灵活组合。
这样带来的好处自然也是明显的
1.标准化 可以定义组件标准化,分工更加精细明确,上手也快,便于扩展
2.复用性 定义标准组件,大大提升开发效率
工具
根据如上描述,我们可以分析下现代前端所需的工具特征
1.类似别的编程语言的独立运行时和标准库——nodejs
2.基于模块的库管理工具——npm/cnpm/yarn
3.将引入的模块/组件等打包成前端部署文件——webpack
nodejs和nvm
官网 https://nodejs.org/zh-cn/
简单的说 Node.js 就是运行在服务端的 JavaScript,基于chrome V8改造而成。
node执行程序简单,和一般解释类型程序语言,比如server.js文件,如下执行
node server.js
建议使用nvm(Node Version Manager) ,nvm是 Nodejs 版本管理器,它让我们方便的对切换Nodejs 版本。
nvm的详细安装以及使用可以访问以下链接:
MAC 上安装使用 brew install nvm
常用nvm命令如下
nvm ls-remote 查看远程库中有哪些node版本可以安装
nvm list 查看当前已安装所有的node 版本
nvm install v10.13.0 安装指定的版本,安装多版本
nvm use —delete-prefix 10.13.0 使用nvm use切换到指定的版本
nvm current 查看当前node版本
nvm which v10.13.0 查看当前版本的安装目录
npm/cnpm/yarn
官网 https://npmmirror.com/
npm是随同NodeJS一起安装的包(模块)管理工具,用于管理包依赖和打包发布。
npm -v
查看npm版本
依赖管理文件
- package.json
npm使用package.json用来记录模块信息,一个典型的文件如下,其中dependencies指明生产环境依赖的包,devDependencies指明开发环境下依赖的包。
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0"
},
"dependencies": {
"lodash": "^4.17.21"
}
}
这里^是版本指定语法
- ~ 会匹配最近的小版本依赖包,比如~1.2.3会匹配所有1.2.x版本,但是不包括1.3.0
- ^ 会匹配最新的大版本依赖包,比如^1.2.3会匹配所有1.x.x的包,包括1.3.0,但是不包括2.0.0
- *这意味着安装最新版本的依赖包
自己创建一个新模块时,可以如下生成默认的package.json
# 需要根据提示手动输入项目的信息
npm init
# 使用默认值创建,不用根据提示手动输入项目的信息,但是当前文件夹名不能是中文
npm init -y
另外很多开源项目,下载后默认只有package.json描述依赖,那么和后端语言maven/pip等一样,可以如下一键下载相关依赖
npm install
npm i # 简写
npm ci # 精确安装
- package.lock.json
现在也会看到同目录一般有package.lock.json,这是因为package.json本版本描述语法,保证了最大兼容性,但是在不同的用户电脑中,初始化的包的版本可能会出现不一样的情况,从而造成未可知的bug。
npm在5.0之后引入了package.lock.json文件,用来固定包的版本、包的源地址等信息,保证在不同的用户开发环境中加载的是相同的包。
一般使用精确安装语法 npm ci
会强制限制从package.lock.json读取安装。
模块管理
# 当前项目依赖安装,在当前文件夹使用,安装的依赖放在 ./node_modules
npm install 模块名称
npm i 模块名称 # 简写
# 全局依赖安装,可以在任意文件夹使用,安装的依赖放在node 的安装目录
npm install --global 模块名称
npm i -g 模块名称 # 简写
npm i 模块名称@版本 # 下载指定的版本
npm install -g cnpm --registry=https://registry.npm.taobao.org #指定源安装
# 卸载模块
npm uninstall 模块名
#查看模块
npm ls
调试开发
- 生产阶段的依赖
–save或-S
将加入安装包信息到package.json文件dependencies对象中,一般是项目运行正式需要的模块安装
npm install gulp --save
npm install gulp -S
- 开发阶段的依赖
–save-dev或-D
将加入安装包信息到package.json文件devDependencies对象中,参考如上文件,一般是开发阶段的辅助工具如webpack vue-cli等的安装
npm install gulp --save-dev
npm install gulp -D
安装升级npm
默认npm随nodejs发布,可如下升级
npm install npm -g
npm默认的镜像比较慢,可如下配置阿里代理代理
npm get registry #查看当前
npm config set registry http://registry.npm.taobao.org/ #阿里代理
npm config set registry https://registry.npmjs.org/ # 设回官方
也可以设置别名安装,即常说的cnpm
npm install -g cnpm --registry=https://registry.npmmirror.com
另一个常见的包管理工具是yarn,核心就是改善并行下载,速度提升较多,但是现在新版npm也大大改善,用法都差不多,感兴趣的可以自行查阅相关资料。
webpack
官网 https://www.webpackjs.com/
如下,现代化前端包含诸多模块,webpack就相当于传统后端语言的编译器,输入的是各种模块,输出的是html+js+css+image组成的可在浏览器执行的前端打包产物。
这个过程中,webpack通过定义rules中不同loader来辅助完成转换打包,参考如下
1.js转换和依赖精简,不同平台版本的ts/js转换保证跨平台运行,一般是babel-loader
2.css转换和兼容性,包括很多css的高级工具sass/less等提供更灵活的书写方式,可编译成原始css,一般是css-loader
3.image相关的转换/加载/切分等,一般是file-loader
4.其他组件处理,比如vue对应的vue-loader
配置文件
webpack.config.js描述配置,一个典型vue项目配置如下
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
],
}, {
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg|eot|woff|woff2|ttf)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
}
...
}
require(‘path’),node自带的一个模块,用来处理路径
entry:入口函数,要处理文件
output:出口,输出文件的位置
resolve()拼接路径
rules指定用于转换处理的各种loader
常用命令
cnpm i -g webpack webpack-cli #全局安装
cnpm i webpack webpack-cli -D #本地调试依赖安装
webpack # 进到目录 运行打包
参考
深入使用已经有太多教程,推荐参考的快速学习文档如下,进一步学习参考官网
https://zhuanlan.zhihu.com/p/163267718
nodejs——https://www.runoob.com/nodejs/nodejs-tutorial.html
npm——https://blog.csdn.net/qq_45677671/article/details/114546577
webpack——https://blog.csdn.net/m0_63722685/article/details/125473926