webpack初步了解
- webpack初步了解
- 建構工具
- 建構工具的功能
- 常用的建構工具
- webpack介紹
- webpack指令
- webpack常用配置介紹
- 出入口 Entry&Output
- entry配置
- output的配置
- 子產品 Module
- 子產品加載(轉換)器 Loader
- loader學習
- 使用loader的三種方式
- babel
- 插件 Plugin
- 出入口 Entry&Output
- webpack-dev-server
- devServer配置項
- webpack插件
- extract-text-webpack-plugin
- 開發/生産環境打包
- webpack項目中的最佳配置
- 建構工具
建構工具
建構工具的功能
- 代碼轉換:TypeScript 編譯成 JavaScript、SCSS 編譯成 CSS 等。
- 檔案優化:壓縮 JavaScript、CSS、HTML 代碼,壓縮合并圖檔等。
- 子產品合并:在采用子產品化的項目裡會有很多個子產品和檔案,需要建構功能把子產品分類合并成一個檔案。
- 代碼分割:提取多個頁面的公共代碼、提取首屏不需要執行部分的代碼讓其異步加載。
- 自動重新整理:監聽本地源代碼的變化,自動重新建構、重新整理浏覽器。
- 代碼校驗:在代碼被送出到倉庫前需要校驗代碼是否符合規範,以及單元測試是否通過。
常用的建構工具
- grunt
- gulp
- fis3(百度)
- webpack子產品化管理工具,可以對子產品進行:
- 壓縮
- 預處理
- 按需打包
- 按需加載
- 熱加載
webpack介紹
- webpack可以看做是子產品打包機,它做的事情是:
- 分析你的項目結構,找到JavaScript子產品;
- 其它的一些浏覽器不能直接運作的拓展語言(Scss,TypeScript等);
- 并将其轉換和打包為合适的格式供浏覽器使用;
- Webpack和Grunt以及Gulp相比有什麼特性
- Webpack和另外兩個并沒有太多的可比性;
- Gulp/Grunt是一種能夠優化前端的開發流程的工具,而WebPack是一種子產品化的解決方案;
- Webpack的優點使得Webpack在很多場景下可以替代Gulp/Grunt類的工具;
- Grunt和Gulp的工作方式是:在一個配置檔案中,指明對某些檔案進行類似編譯,組合,壓縮等任務的具體步驟,工具之後可以自動替你完成這些任務。
- Webpack的工作方式是:把你的項目當做一個整體,通過一個給定的主檔案(如:index.js),Webpack将從這個檔案開始找到你的項目的所有依賴檔案,使用loaders處理它們,最後打包為一個(或多個)浏覽器可識别的JavaScript檔案。
- webpack特征
- 插件化:webpack本身非常靈活,提供了豐富的插件接口。基于這些接口,webpack開發了很多插件作為内置功能
- 速度快:webpack使用異步IO以及多級緩存機制。是以webpack的速度是很快的,尤其是增量更新。
- 豐富的Loaders:loaders用來對檔案做預處理。這樣webpack就可以打包任何靜态檔案。
- 高适配性:webpack同時支援AMD/CommonJs/ES6子產品方案。webpack會靜态解析你的代碼,自動幫你管理他們的依賴關系。此外,webpack對第三方庫的相容性很好。
- 代碼拆分:webpack可以将你的代碼分片,進而實作按需打包。這種機制可以保證頁面隻加載需要的JS代碼,減少首次請求的時間。
- 優化:webpack提供了很多優化機制來減少打包輸出的檔案大小,不僅如此,它還提供了hash機制,來解決浏覽器緩存問題。
- 開發模式友好:webpack為開發模式也提供了很多輔助功能。比如SourceMap、熱更新等。
- 使用場景多:webpack不僅适用于web應用場景,也适用于Webworkers、Node.js場景
webpack指令
- webpack 執行一次開發時的編譯
- webpack -p 執行一次生成環境的編譯(壓縮)
- webpack –watch 在開發時持續監控增量編譯(很快)
- webpack -d 讓他生成SourceMaps
- webpack –progress 顯示編譯進度
- webpack –colors 顯示靜态資源的顔色
- webpack –display-chunks 展示編譯後的分塊
- webpack –display-modules 列出打包子產品
- webpack –display-reasons 顯示更多引用子產品原因
- webapck –display-error-details 顯示更多報錯資訊
webpack常用配置介紹
出入口 Entry&Output
- entry:入口
- 關聯的很多其他需要打包的檔案
- output:出口
-
path:檔案打出的路徑,這個path是nodejs内置的方法;
entry和output配置
-
entry配置
- entry是指需要打包的檔案;
- entry有幾種使用方法:
- 字元串
entry:__dirname+'/src/script/main.js'
//對應
output:{
path:__dirname+'/dist/js',
filename:'bundle.js'
}
- 數組
entry:[
__dirname+'/src/script/main.js',
__dirname+'/src/script/a.js'
]
//對應
output:{
path:__dirname+'/dist/js',
filename:'bundle.js'
}
- 對象
entry:{
main:__dirname+'/src/script/main.js',
a:__dirname+'/src/script/a.js'
}
//對應
output:{
path:__dirname+'/dist/js',
filename:'[name]-[hash].js'
}
//or
output:{path:__dirname+'/dist/js',filename:'[name]-[chunkhash].js'}
output的配置
- output是指打包生成的檔案
- entry中輸入多個chunk時,為確定檔案名唯一避免互相覆寫使用占位符命名filename;
- 三種占位符
- [name]是chunk的name;
- [hash]是本次打包的hash值,hash值相同;
- [chunkhash]是每個chunk的hash值,不同檔案同次打包不相同,保證檔案的唯一性,隻有改變檔案中的内容時hash才變化,未做改變的檔案hash值不變;
子產品 Module
- module:子產品,在 Webpack眼裡一切皆子產品,預設隻識别js檔案, 如果是其它類型檔案利用對應的loader轉換為js子產品。
- rules:做很多的規定,比如:加載css、将es6編譯成es5;
- “-loader”其實是可以省略不寫的,多個loader之間用“!”連接配接起來
module: { //加載器配置 loaders: [ //.css 檔案使用 style-loader 和 css-loader 來處理 { test: /\.css$/, loader: 'style-loader!css-loader' }, //.js 檔案使用 jsx-loader 來編譯處理 { test: /\.js$/, loader: 'jsx-loader?harmony' }, //.scss 檔案使用 style-loader、css-loader 和 sass-loader 來編譯處理 { test: /\.scss$/, loader: 'style!css!sass?sourceMap'}, //圖檔檔案使用 url-loader 來處理,小于8kb的直接轉為base64 { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} ] }
- webpack本身隻能加載js子產品,如果需要加載其他類型的檔案(子產品),就需要使用對應的loader進行轉換/加載;
- 如果我們想要在js檔案中通過require引入子產品,比如css或image,那麼就需要在這裡配置加載器,這一點對于React來說相當友善,因為可以在元件中使用子產品化CSS。而一般的項目中可以不用到這個加載器。
- module 的作用是添加loaders, 那loaders有什麼作用呢?
子產品加載(轉換)器 Loader
- loader:子產品加載器,将非js子產品包裝成webpack能了解的js子產品;
- loader用于轉換應用程式的資源檔案。他們是運作在nodejs下的函數,使用參數來擷取一個資源的來源,并且傳回一個新的來源(資源的位置)。
- 檔案loader:
- url-loader 像 file loader 一樣工作,但如果檔案小于限制,可以傳回 data URL
- file-loader 将檔案發送到輸出檔案夾,并傳回(相對)URL
- JSON的loader
- json-loader 加載 JSON 檔案(預設包含)
- ES5-6的loader
- babel-loader 加載 ES2015+ 代碼,然後使用 Babel 轉譯為 ES5
- css的loader
- style-loader 将子產品的導出作為樣式添加到 DOM 中
- css-loader 解析 CSS 檔案後,使用 import 加載,并且傳回 CSS 代碼
- less-loader 加載和轉譯 LESS 檔案
- sass-loader 加載和轉譯 SASS/SCSS 檔案
- stylus-loader 加載和轉譯 Stylus 檔案
- 代碼規範loader
- eslint-loader PreLoader,使用 ESLint 清理代碼
- vue的loader
- vue-loader 加載和轉譯 Vue 元件
- resolve
- alias可以用于定義别名,用過seajs等子產品工具的都知道alias的作用,比如我們在這裡定義了ui這個别名,那麼在子產品中想引用ui目錄下的檔案,就可以直接這樣寫:
不用加上前面的更長的檔案路徑。require('ui/dialog.js');
- alias可以用于定義别名,用過seajs等子產品工具的都知道alias的作用,比如我們在這裡定義了ui這個别名,那麼在子產品中想引用ui目錄下的檔案,就可以直接這樣寫:
resolve: {
//查找module的話從這裡開始查找
root: 'E:/github/flux-example/src', //絕對路徑
//自動擴充檔案字尾名,意味着我們require子產品可以省略不寫字尾名
extensions: ['', '.js', '.json', '.scss'],
//子產品别名定義,友善後續直接引用别名,無須多寫長長的位址
alias: {
AppStore : 'js/stores/AppStores.js',//後續直接 require('AppStore') 即可
ActionType : 'js/actions/ActionType.js',
AppAction : 'js/actions/AppAction.js'
}
}
loader學習
- 學習網址:https://webpack.js.org/concepts/loaders/;
- Webpack 本身隻能處理原生的js子產品,但是loader轉換器可以将各種類型的資源轉換成js子產品。這樣,任何資源都可以成為Webpack可以處理的子產品。
使用loader的三種方式
- 在檔案中直接引入loader檔案,es6的文法
//es6的文法
import Styles from 'style-loader!css-loader?modules!./styles.css';
//commonjs文法
require('style-loader!css-loader?modules!./styles.css')
- 在指令行界面使用的方式CLI
//直接執行
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
- 在配置檔案中使用loader
module: {
rules: [
{
<!-- 首先對資源一個正則比對 -->
test: /\.css$/,
<!-- 比對成功後會使用多個loader對其進行處理 -->
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}
babel
- Babel通過文法轉換器支援最新版本的JavaScript。這些插件允許你立刻使用新文法,無需等待浏覽器支援。
- 在src檔案夾下建立component檔案夾,component裡面建立layer元件,元件裡有js、css、html檔案;
- 在src檔案夾中建立入口js檔案,app.js;
//app.js中引入html
import layer from './component/layer/layer.js'
const App = function() {
console.log(layer)
}
new App()
//layer.js中渲染模闆
import tpl from './layer.html'
function layer() {
return {
name: 'layer',
tpl: tpl
}
}
export default layer;
- 安裝babel轉化es6的代碼
(webpack3版本)npm install --save-dev babel-loader babel-core
- 使用babel加載器将es6的文法進行轉化,轉化時要指定參數,方式有三種:
- 使用配置指定
//在配置webpack.config.js中添加
module: {
rules: [{
test: /\.js$/,
loader: "babel-loader",
//加快打包速度的配置
//排除範圍
exclude: __dirname + '/node_modules/',
//babel-loader的處理範圍
include: '/src/',
//webpack3中使用options代替query
options: {
'presets': ['env']
}
}]
}
- 在package.json中添加
"babel":{
"presets":["env"];
}
- 根目錄下建立.babelrc檔案
{
"presets": ["env"]
}
插件 Plugin
- plugin:插件,在webpack建構流程中的特定時機插入具有特定功能的代碼;
//使用了CommonsChunkPlugin用于生成公用代碼,不隻可以生成一個,還能根據不同頁面的檔案關系,自由生成多個,例如:
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
entry: {
p1: "./page1",
p2: "./page2",
p3: "./page3",
ap1: "./admin/page1",
ap2: "./admin/page2"
},
output: {
filename: "[name].js"
},
plugins: [
new CommonsChunkPlugin("admin-commons.js", ["ap1", "ap2"]),
new CommonsChunkPlugin("commons.js", ["p1", "p2", "admin-commons.js"])
]
};
// 在不同頁面用<script>标簽引入如下js:
// page1.html: commons.js, p1.js
// page2.html: commons.js, p2.js
// page3.html: p3.js
// admin-page1.html: commons.js, admin-commons.js, ap1.js
// admin-page2.html: commons.js, admin-commons.js, ap2.js
- 例:
module.exports = {
devtool: "source-map", //生成sourcemap,便于開發調試
entry: getEntry(), //擷取項目入口js檔案
output: {
path: path.join(__dirname, "dist/js/"), //檔案輸出目錄
publicPath: "dist/js/", //用于配置檔案釋出路徑,如CDN或本地伺服器
filename: "[name].js", //根據入口檔案輸出的對應多個檔案名
},
module: {
//各種加載器,即讓各種檔案格式可用require引用
loaders: [
// { test: /\.css$/, loader: "style-loader!css-loader"},
// { test: /\.less$/, loader: "style-loader!csss-loader!less-loader"}
]
},
resolve: {
//配置别名,在項目中可縮減引用路徑
alias: {
jquery: srcDir + "/js/lib/jquery.min.js",
core: srcDir + "/js/core",
ui: srcDir + "/js/ui"
}
},
plugins: [
//提供全局的變量,在子產品中使用無需用require引入
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery",
// nie: "nie"
}),
//将公共代碼抽離出來合并為一個檔案
new CommonsChunkPlugin('common.js'),
//js檔案的壓縮
new uglifyJsPlugin({
compress: {
warnings: false
}
})
]
};
webpack-dev-server
- webpack-dev-server是一個輕量級的伺服器,修改檔案源碼後,自動重新整理頁面将修改同步到頁面上;
- webpack-dev-server的他爹和他爹的朋友
- webpack-dev-middleware:作為一個 webpack 中間件,它會開啟 watch mode 監聽檔案變更,并自動地在記憶體中快速地重新打包、提供新的 bundle,自動編譯(watch mode)+速度快(全部走記憶體)。
- webpack-hot-middleware:
- webpack 可以通過配置 webpack.HotModuleReplacementPlugin 插件來開啟全局的 HMR 能力;
- 開啟後 bundle 檔案會變大一些,因為它加入了一個小型的 HMR 運作時(runtime),當你的應用在運作的時候,webpack 監聽到檔案變更并重新打包子產品時,HMR 會判斷這些子產品是否接受 update,若允許,則發信号通知應用進行熱替換。
- webpack-dev-server是一個小型的Node.js Express伺服器,它使用webpack-dev-middleware來服務于webpack的包,除此自外,它還有一個通過Sock.js來連接配接到伺服器的微型運作時
- webpack-dev-server是一個獨立的NPM包,你可以通過npm install webpack-dev-server來安裝它。
- webpack-dev-server配置
var WebpackDevServer = require("webpack-dev-server");
var webpack = require("webpack");
var compiler = webpack({});
var server = new WebpackDevServer(compiler, {
contentBase: "/path/to/directory",
hot: true,
historyApiFallback: false,
compress: true,
proxy: {
"**": "http://localhost:9090"
},
setup: function(app) {},
staticOptions: {},
quiet: false,
noInfo: false,
lazy: true,
filename: "bundle.js",
watchOptions: {
aggregateTimeout: ,
poll:
},
publicPath: "/assets/",
headers: { "X-Custom-Header": "yes" },
stats: { colors: true }
});
server.listen(, "localhost", function() {});
devServer配置項
- contentBase
- 即 SERVERROOT,如 “path.join(__dirname, “src/html”)”,後續通路 http://localhost:3333/index.html 時,SERVER 會從 src/html 下去查找 index.html 檔案。
- 它可以是單個或多個位址的形式:(若不填寫該項,預設為項目根目錄。)
//單個
contentBase: path.join(__dirname, "public")
//多個:
contentBase: [path.join(__dirname, "public"), path.join(__dirname, "assets")]
- port
- 即監聽端口,預設為8080。
- compress
- 傳入一個 boolean 值,通知 SERVER 是否啟用 gzip。
- hot
- 傳入一個 boolean 值,通知 SERVER 是否啟用 HMR。
- https
- 可以傳入 true 來支援 https 通路,也支援傳入自定義的證書:
https: true
//也可以傳入一個對象,來支援自定義證書
https: {
key: fs.readFileSync("/path/to/server.key"),
cert: fs.readFileSync("/path/to/server.crt"),
ca: fs.readFileSync("/path/to/ca.pem"),
}
- proxy
- 代理配置,适用場景是,除了 webpack-dev-server 的 SERVER(SERVER A) 之外,還有另一個在運作的 SERVER(SERVER B),而我們希望能通過 SERVER A 的相對路徑來通路到 SERVER B 上的東西。
devServer: {
contentBase: path.join(__dirname, "src/html"),
port: ,
hot: true,
proxy: {
"/api": "http://localhost:5050"
}
}
//運作 webpack-dev-server 後,若通路 http://localhost:3333/api/user,則相當于通路 http://localhost:5050/api/user。
- publicPath
- 如同 webpack-dev-middleware 的 publicPath 一樣,表示從記憶體中的哪個路徑去存放和檢索靜态檔案;
- 不過官方文檔有一處錯誤需要堪正 —— 當沒有配置 devServer.publicPath 時,預設的 devServer.publicPath 并非根目錄,而是 output.publicPath;
- 這也是為何咱們的例子裡壓根沒寫 devServer.publicPath,但還能正常請求到 https://localhost:3333/assets/bundle.js。
- setup
- webpack-dev-server 的服務應用層使用了 express,故可以通過 express app 的能力來模拟資料回包,devServer.setup 方法就是幹這事的:
devServer: {
contentBase: path.join(__dirname, "src/html"),
port: ,
hot: true,
setup(app){ //模拟資料
app.get('/getJSON', function(req, res) {
res.json({ name: 'vajoy' });
});
}
}
webpack插件
- webpack 通過 plugins 實作各種功能。常見的 plugins 如下:
- webpack.DefinePlugin 定義環境變量;
const webpack = require('webpack'); const NODE_ENV = process.env.NODE_ENV; // 從指令行環境擷取 NODE_ENV 參數 module.exports = { plugins: [ new webpack.DefinePlugin({ 'process.env': { 'NODE_ENV': JSON.stringify(NODE_ENV) } // 定義浏覽器中的替換的變量為 `process.env.NODE_ENV` }) ] }
- webpack.EnvironmentPlugin 定義環境變量;
const webpack = require('webpack'); module.exports = { plugins: [ new webpack.EnvironmentPlugin([ 'NODE_ENV' ]) ] }
- webpack.optimize.CommonsChunkPlugin 共用 js 打包
- html-webpack-plugin 使用模版生成 html 檔案
- webpack-visualizer-plugin 輸出依賴檔案分析圖表
- webpack.HotModuleReplacementPlugin 代碼熱更新,用于調試模式
- webpack.optimize.OccurrenceOrderPlugin 調整子產品的打包順序,用到次數更多的會出現在檔案的前面
- webpack.NoErrorsPlugin 建構過程中有報錯,不認為建構完成
- webpack.ProgressPlugin 輸出建構進度
- webpack.BannerPlugin 在檔案頭添加注釋
- webpack.optimize.UglifyJsPlugin 壓縮 js
- webpack.optimize.DedupePlugin 去除重複依賴
- extract-text-webpack-plugin 從 js 中提取出樣式檔案,單獨打包成 css 檔案;
- clean-webpack-plugin每次運作清除指定目錄下打封包件;
extract-text-webpack-plugin
- 作用:該插件的主要是為了抽離css樣式,防止将樣式打包在js中引起頁面樣式加載錯亂的現象;
- 安裝:
npm install extract-text-webpack-plugin --save-dev
- 插件參數:
- use:指需要什麼樣的loader去編譯檔案,這裡由于源檔案是.css是以選擇css-loader
- fallback:編譯後用什麼loader來提取css檔案
- publicfile:用來覆寫項目路徑,生成該css檔案的檔案路徑
- 使用:
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader", // 編譯後用什麼loader來提取css檔案
use: "css-loader" // 指需要什麼樣的loader去編譯檔案,這裡由于源檔案是.css是以選擇css-loader
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
]
}
開發/生産環境打包
- webpack.base.conf.js
- 開啟source Map ,幫助調試
webpack項目中的最佳配置
- webpack官方提供的配置方法是通過module.exports傳回一個json,但是這種場景不靈活,不能适配多種場景。
- 比如要解決:production模式和development模式,webpack的配置是有差異的,大緻有兩種思路。
- 1、兩份配置檔案
,然後不同場景下,使用不同的配置檔案。webpack.config.production.js/webpack.config.development.js
- 2、通過module.exports傳回函數,該函數能接受參數。
- 相對來說,第一種更簡單,但是重複配置多;第二種更靈活,推薦第二種方式。
- 1、兩份配置檔案
//傳回函數的方式的配置代碼架子如下:
module.exports = function(env) {
return {
//上下文
context: config.context,
//入口檔案,是所有依賴關系的入口,webpack從這個入口開始靜态解析,分析子產品之間的依賴關系。
entry: config.src,
//打包輸出的配置
output: {
path: path.join(config.jsDest, project),
filename: '[name].js',
chunkFilename: '[name].[chunkhash:8].js',
publicPath: '/assets/' + project + '/'
},
//SourceMap選項,便于開發模式下調試。
devtool: "eval",
//監聽模式,增量更新,開發必備!
watch: false,
//優化。
profile: true,
//webpack建構的過程中會生成很多臨時的檔案,打開cache可以讓這些臨時的檔案緩存起來,進而更快的建構。
cache: true,
//loaders用來對檔案做預處理。這樣webpack就可以打包任何靜态檔案。
module: {
loaders: getLoaders(env)
},
//子產品别名,這樣可以更友善的引用子產品。
resolve: {
alias: getAlias(env)
},
//webpack的一些内置功能均是以插件的形式提供。
plugins: getPlugins(env)
};
}
gitHub參考位址:https://github.com/mutouafangzi/webpackDemo20180222