Node.js
Node.js 是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行時(shí),讓 JavaScript 可以在脫離瀏覽器的環(huán)境下運(yùn)行。
Node.js 同時(shí)提供了很多常用操作方法和全局變量,可以在代碼中直接使用,比如 require,console.log,__dirname,setInterval,setTimeout 等等,可以認(rèn)為是前后端項(xiàng)目中的 JS 開(kāi)發(fā)包。
具體文檔參考:Node.js 中文網(wǎng)
NPM
npm 全稱為 Node Package Manager,是一個(gè)基于 Node.js 實(shí)現(xiàn)的包管理器,也是整個(gè) Node.js 社區(qū)最流行、支持的第三方模塊最多的包管理器。一般安裝完 Node.js 時(shí),默認(rèn)就帶了 npm,npm 常用命令如下:
npm init
創(chuàng)建并初始化一個(gè) package.json 文件,package.json 文件是一個(gè)純 JSON 文件,描述了項(xiàng)目中的一些信息,比如項(xiàng)目名稱,版本號(hào),第三方依賴,腳本信息等,一個(gè)簡(jiǎn)單的示例:
{
"name": "my_package",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/ashleygwilliams/my_package.git"
},
"dependencies" :
{
"foo" : "1.0.0 - 2.9999.9999",
"bar" : ">=1.0.2 <2.1.2",
"PackageA": "1.0.0"
},
"devDependencies": {
"coffee-script": "~1.6.3"
},
"peerDependencies": {
"PackageB": "1.0.0"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/ashleygwilliams/my_package/issues"
},
"homepage": "https://github.com/ashleygwilliams/my_package"
}
關(guān)于 peerDependencies,考慮如下使用場(chǎng)景
比如我們項(xiàng)目的依賴 PackageA,而 PackageA 依賴 PackageB,如果不使用 peerDependencies 話,那么安裝后的目錄是這樣的:
MyProject
|- node_modules
|- PackageA
|- node_modules
|- PackageB
在項(xiàng)目中可以通過(guò) require(PackageA) 使用 A,但是無(wú)法直接通過(guò) require(PackageB) 使用 B,因?yàn)?Node 默認(rèn)在 node_modules 目錄下找
通過(guò)使用 peerDependencies 描述 PackageB,那么安裝后項(xiàng)目是這樣的:
MyProject
|- node_modules
|- PackageA
|- PackageB
npm install <package>
# 全局安裝
npm install -g jshint
# 本地安裝
npm install jshint
# 本地安裝,并在 package.json 中添加 dependencies 依賴
npm install jshint --save
# 本地安裝,并在 package.json 中添加 devDependencies 依賴
npm install jshint --save-dev
devDependencies 依賴是本地使用的依賴,不需要打包到項(xiàng)目中
npm uninstall <package>
# 卸載全局安裝包
npm uninstall -g lodash
# 卸載本地安裝包
npm uninstall lodash
# 卸載本地安裝包并從 package.json 中刪除 dependencies 依賴
npm uninstall --save lodash
# 卸載本地安裝包并從 package.json 中刪除 devDependencies 依賴
npm uninstall --save-dev lodash
npm update <package>
# 更新全局包
npm update -g lodash
# 更新本地 package.json 中制定的依賴包
npm update
npm run-script <command>
別名 npm run,執(zhí)行 package.json 中定義的腳本,對(duì)于 test, start, restart, stop 名稱,可以省略 run,
npm start
npm run start
npm run build
需要注意的是,對(duì)于本地命令工具,npm 默認(rèn)會(huì)對(duì)腳本添加 node_modules/.bin 路徑,不需要自己顯示指定
{
"scripts": {
"test": "tap test/\*.js",
"test": "node_modules/.bin/tap test/\*.js" # 不需要這么寫(xiě)
}
}
Webpack
webpack 是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)。當(dāng) webpack 處理應(yīng)用程序時(shí),它會(huì)遞歸地構(gòu)建一個(gè)依賴關(guān)系圖(dependency graph),其中包含應(yīng)用程序需要的每個(gè)模塊,然后將所有這些模塊打包成一個(gè)或多個(gè) bundle 文件。
安裝
# 全局安裝
npm install -g webpack
# 安裝到當(dāng)前項(xiàng)目目錄(一般使用這種方式)
npm install --save-dev webpack
使用
新建項(xiàng)目 WebpackDemo:
WebpackDemo
|- node_modules
|- app
|- main.js
|- public
|- index.html
|- package.json
基礎(chǔ)用法
# {extry file} 入口文件的路徑,本文中就是上述main.js的路徑,
# {destination for bundled file} 打包文件的存放路徑
webpack {entry file} {destination for bundled file}
# 示例
node_modules/.bin/webpack app/main.js public/bundle.js
配置文件 webpack.config.js
通過(guò)創(chuàng)建 webpack.config.js 配置文件,來(lái)簡(jiǎn)化 webpack 的使用。
首先先了解 webpack 的四個(gè)核心概念:entry,output,loader,plugins
entry
entry 指示 webpack 應(yīng)該使用哪個(gè)模塊,來(lái)作為構(gòu)建其內(nèi)部依賴圖的開(kāi)始,默認(rèn)值為 ./src。
進(jìn)入入口起點(diǎn)后,webpack 會(huì)找出有哪些模塊和庫(kù)是入口起點(diǎn)(直接和間接)依賴的。每個(gè)依賴項(xiàng)隨即被處理,最后輸出到稱之為 bundles 的文件中。
module.exports = {
entry: './app/main.js'
};
output
output 屬性告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles,以及如何命名這些文件,默認(rèn)值為 ./dist?;旧?,整個(gè)應(yīng)用程序結(jié)構(gòu),都會(huì)被編譯到你指定的輸出路徑的文件夾中。你可以通過(guò)在配置中指定一個(gè) output 字段,來(lái)配置這些處理過(guò)程:
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"http://打包后輸出文件的文件名
}
}
注:“__dirname” 是 node.js 中的一個(gè)全局變量,它指向當(dāng)前執(zhí)行腳本所在的目錄。
有了上面這個(gè)配置文件后,之前的命令就可以改成:
node_modules/.bin/webpack
結(jié)合前面講的 package.json,通過(guò)配置 scripts 字段可以進(jìn)一步簡(jiǎn)化:
{
"name": "webpack-sample-project",
"version": "1.0.0",
"scripts": {
"start": "webpack" // 這里不需要指定路徑,Node 自動(dòng)會(huì)從 node_modules/.bin 目錄下找
},
"devDependencies": {
"webpack": "3.10.0"
}
}
現(xiàn)在我們打包只需要輸入
npm start
前面說(shuō)過(guò),start 是一個(gè)特殊的命令,不需要加 run
loader
loader 讓 webpack 能夠去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以將所有類型的文件轉(zhuǎn)換為 webpack 能夠處理的有效模塊,然后你就可以利用 webpack 的打包能力,對(duì)它們進(jìn)行處理。
通過(guò)使用不同的loader,webpack有能力調(diào)用外部的腳本或工具,實(shí)現(xiàn)對(duì)不同格式的文件的處理,比如說(shuō)分析轉(zhuǎn)換scss為css,或者把下一代的JS文件(ES6,ES7)轉(zhuǎn)換為現(xiàn)代瀏覽器兼容的JS文件,對(duì)React的開(kāi)發(fā)而言,合適的Loaders可以把React的中用到的JSX文件轉(zhuǎn)換為JS文件。
本質(zhì)上,webpack loader 將所有類型的文件,轉(zhuǎn)換為應(yīng)用程序的依賴圖(和最終的 bundle)可以直接引用的模塊。
loaders 需要單獨(dú)安裝并且需要在 webpack.config.js 中的 modules 關(guān)鍵字下進(jìn)行配置,Loaders 的配置包括以下幾方面:
- test:一個(gè)用以匹配loaders所處理文件的拓展名的正則表達(dá)式(必須)
- loader:loader的名稱(必須)
- include/exclude:手動(dòng)添加必須處理的文件(文件夾)或屏蔽不需要處理的文件(文件夾)(可選);
- query:為loaders提供額外的設(shè)置選項(xiàng)(可選)
安裝對(duì)應(yīng)的 loader
npm install --save-dev css-loader
npm install --save-dev ts-loader
webpack.config.js
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' }
]
}
};
常見(jiàn) loader
Babel
Babel其實(shí)是一個(gè)編譯JavaScript的平臺(tái),它可以編譯代碼幫你達(dá)到以下目的:
- 讓你能使用最新的JavaScript代碼(ES6,ES7...),而不用管新標(biāo)準(zhǔn)是否被當(dāng)前使用的瀏覽器完全支持;
- 讓你能使用基于JavaScript進(jìn)行了拓展的語(yǔ)言,比如React的JSX;
Babel 其實(shí)是幾個(gè)模塊化的包,其核心功能位于稱為babel-core 的 npm 包中,webpack 可以把其不同的包整合在一起使用,對(duì)于每一個(gè)你需要的功能或拓展,你都需要安裝單獨(dú)的包(用得最多的是解析Es6的 babel-env-preset 包和解析 JSX 的 babel-preset-react 包)。
安裝 babel
npm install --save react react-dom
npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react
webpack.config.js
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"http://打包后輸出文件的文件名
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
}
]
}
};
通過(guò)上面配置,我們就可以在項(xiàng)目中使用 React 以及 JSX 語(yǔ)法了
關(guān)于 Babel,參考 Babel 中文文檔
css-loader 和 style-loader
css-loader 使你能夠使用類似 @import 和 url(...) 的方法實(shí)現(xiàn) require() 的功能,style-loader 將所有的計(jì)算后的樣式加入頁(yè)面中,二者組合在一起使你能夠把樣式表嵌入webpack打包后的JS文件中。
# 安裝
npm install --save-dev style-loader css-loader
// 使用
module.exports = {
...
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader"
}
]
}
]
}
};
請(qǐng)注意這里對(duì)同一個(gè)文件引入多個(gè)loader的方法。
CSS module
被稱為 CSS modules 的技術(shù)意在把 JS 的模塊化思想帶入 CSS 中來(lái),通過(guò) CSS 模塊,所有的類名,動(dòng)畫(huà)名默認(rèn)都只作用于當(dāng)前模塊。Webpack 對(duì) CSS 模塊化提供了非常好的支持,只需要在 CSS loader 中進(jìn)行簡(jiǎn)單配置即可,然后就可以直接把 CSS 的類名傳遞到組件的代碼中,這樣做有效避免了全局污染。具體的代碼如下:
{
test: /\.css$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: true, // 指定啟用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的類名格式
}
}]
}
使用示例:
/* Greeter.css */
.root {
background-color: #eee;
padding: 10px;
border: 3px solid #ccc;
}
/* Greeter.js */
import React, {Component} from 'react';
import config from './config.json';
import styles from './Greeter.css';//導(dǎo)入
class Greeter extends Component{
render() {
return (
<div className={styles.root}> //使用cssModule添加類名的方法
{config.greetText}
</div>
);
}
}
export default Greeter
CSS預(yù)處理器
Sass 和 Less 之類的預(yù)處理器是對(duì)原生 CSS 的拓展,它們?cè)试S你使用類似于 variables, nesting, mixins, inheritance 等不存在于 CSS 中的特性來(lái)寫(xiě) CSS,CSS 預(yù)處理器可以這些特殊類型的語(yǔ)句轉(zhuǎn)化為瀏覽器可識(shí)別的CSS語(yǔ)句,只需要在webpack里使用相關(guān) loaders 進(jìn)行配置就可以使用了,以下是常用的CSS 處理loaders:
- Less Loader
- Sass Loader
- Stylus Loader
plugins
插件(Plugins)是用來(lái)拓展 Webpack 功能的,它們會(huì)在整個(gè)構(gòu)建過(guò)程中生效,執(zhí)行相關(guān)的任務(wù)。
Loaders 和 Plugins 常常被弄混,但是他們其實(shí)是完全不同的東西,loaders是在打包構(gòu)建過(guò)程中用來(lái)處理源文件的(JSX,Scss,Less..),一次處理一個(gè),plugin 并不直接操作單個(gè)文件,它直接對(duì)整個(gè)構(gòu)建過(guò)程其作用,比如打包優(yōu)化,壓縮,重新定義變量等。
Webpack 有很多內(nèi)置插件,同時(shí)也有很多第三方插件,可以讓我們完成更加豐富的功能。
插件使用
- 通過(guò) npm 安裝(非內(nèi)置插件)
- 配置 webpack.config.js 文件
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通過(guò) npm 安裝
const webpack = require('webpack'); // 用于訪問(wèn)內(nèi)置插件
const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'}),
new webpack.BannerPlugin('版權(quán)所有,翻版必究')
]
};
module.exports = config;
插件介紹
HtmlWebpackPlugin
這個(gè)插件的作用是依據(jù)一個(gè)簡(jiǎn)單的index.html模板,生成一個(gè)自動(dòng)引用你打包后的JS文件的新index.html。這在每次生成的js文件名稱不同時(shí)非常有用(比如添加了hash值)。
Hot Module Replacement
許你在修改組件代碼后,自動(dòng)刷新實(shí)時(shí)預(yù)覽修改后的效果。
其他優(yōu)化插件
webpack 提供了一些在發(fā)布階段非常有用的優(yōu)化插件,它們大多來(lái)自于 webpack 社區(qū),可以通過(guò) npm 安裝,通過(guò)以下插件可以完成產(chǎn)品發(fā)布階段所需的功能
- OccurenceOrderPlugin :為組件分配ID,通過(guò)這個(gè)插件webpack可以分析和優(yōu)先考慮使用最多的模塊,并為它們分配最小的ID
- UglifyJsPlugin:壓縮JS代碼;
- ExtractTextPlugin:分離CSS和JS文件
其他功能
Source Maps
開(kāi)發(fā)總是離不開(kāi)調(diào)試,方便的調(diào)試能極大的提高開(kāi)發(fā)效率,不過(guò)有時(shí)候通過(guò)打包后的文件,你是不容易找到出錯(cuò)了的地方,對(duì)應(yīng)的你寫(xiě)的代碼的位置的,Source Maps就是來(lái)幫我們解決這個(gè)問(wèn)題的。
通過(guò)簡(jiǎn)單的配置,webpack就可以在打包時(shí)為我們生成的source maps,這為我們提供了一種對(duì)應(yīng)編譯文件和源文件的方法,使得編譯后的代碼可讀性更高,也更容易調(diào)試。
在webpack的配置文件中配置source maps,需要配置devtool,它有以下四種不同的配置選項(xiàng),各具優(yōu)缺點(diǎn),描述如下:
[圖片上傳失敗...(image-ab92c1-1607478774018)]
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
}
}
構(gòu)建本地服務(wù)器
Webpack提供一個(gè)可選的本地開(kāi)發(fā)服務(wù)器,這個(gè)本地服務(wù)器基于 node.js 構(gòu)建,可以實(shí)現(xiàn)監(jiān)聽(tīng)修改自動(dòng)刷新的功能,不過(guò)它是一個(gè)單獨(dú)的組件,在 webpack中進(jìn)行配置之前需要單獨(dú)安裝它作為項(xiàng)目依賴:
npm install --save-dev webpack-dev-server
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
},
devServer: {
contentBase: "./public",//本地服務(wù)器所加載的頁(yè)面所在的目錄
historyApiFallback: true,//不跳轉(zhuǎn)
inline: true//實(shí)時(shí)刷新
}
}
package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack",
"server": "webpack-dev-server --open"
},
在終端中輸入npm run server即可在本地的8080端口查看結(jié)果
發(fā)布
發(fā)布階段主要是做一些優(yōu)化,比如代碼壓縮,去掉調(diào)試等,dev 和 release 可以提供不同的 webpack.config.js 文件。