文章目錄
最簡單的例子
// main.js
document.write('<h1>Hello World</h1>');
配置文件webpack.config.js
module.exports = {
entry: {
bundle1: './main1.js'
},
output: {
path: __dirname +"/dist/",
filename: 'boundle1.js'
}
};
filename
用於輸出文件的文件名。
目標輸出目錄 path
的絕對路徑。
__dirname
是nodejs的語法糖,獲取當前路徑。
非常明顯,entry
這個對象負責寫入口文件。格式爲屬性名:文件相對路徑,這裏屬性名可以在出口文件用到。
webpack
入口文件可以設置多個,但是輸出文件只能一個,那輸出文件如何做到多個文件命名呢?
多入口文件
有多個文件入口的時候,我們可以這樣寫:
配置文件webpack.config.js
module.exports = {
entry: {
bundle1: './main1.js',
bundle2: './main2.js',
},
output: {
path: __dirname +"/dist/",
filename: '[name].js'
}
};
輸出結果:
明顯,[name]
對應的是入口文件的屬性名,這樣多個入口文件我們只需要設置好屬性名就可以找到對應的輸出文件了。
除了這個之外,webpack
還提供了其他的語法糖。
模板 | 描述 |
---|---|
[hash] | 模塊標識符(module identifier)的 hash |
[chunkhash] | chunk 內容的 hash |
[name] | 模塊名稱 |
[id] | 模塊標識符(module identifier) |
[query] | 模塊的 query,例如,文件名 ? 後面的字符串 |
這裏官方文檔有詳細的說明就不在贅敘。
爲什麼要有這些設置呢?
其實都是爲了實現緩存更新,大部分瀏覽器都是對比緩存,就是會提前檢測服務器的緩存是否更新,如果更新則就從服務器下載文件。
一般檢測更新都是從修改日期和文件名下手,如果2者發生了變化,就重新修改Last-Modifed
的值,這樣客戶端重新請求的時候就會知道文件已經修改過了,需要重新下載文件。
Loader
-
讓 webpack 能夠去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以將所有類型的文件轉換爲 webpack 能夠處理的有效模塊,然後你就可以利用 webpack 的打包能力,對它們進行處理。
-
本質上,webpack loader 將所有類型的文件,轉換爲應用程序的依賴圖(和最終的 bundle)可以直接引用的模塊。
-
loader 可以將文件從不同的語言(如 TypeScript)轉換爲 JavaScript,或將內聯圖像轉換爲 data URL。loader 甚至允許你直接在 JavaScript 模塊中 import CSS文件!
我們從例子來學習
- 處理react的JSX文件
jsx
文件
const React = require('react');
const ReactDOM = require('react-dom');
ReactDOM.render(
<h1>Hello, world!</h1>,
document.querySelector('#wrapper')
);
配置文件webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
}
]
}
};
入口文件有jsx
文件,和上面區別在於多了一個module
,裏面有一個rules
的數組,裏面就是設置了所有的loader
的規則。
test
使用正則去匹配需要處理的文件,找到之後就需要設置使用的處理方式和插件use
,設置了使用babel-loader
能夠將JSX/ES6文件轉爲普通的JS文件,而options
是設置babel-loader
的模式。
- 處理css文件
app.css
body {
background-color: blue;
}
main.js
require('./app.css');
配置文件webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
]
}
};
一開始看到這裏會覺得太麻煩了,首先需要一個入口文件導入css文件,然後到處一個js文件,最後在html引入一個js文件,爲什麼需要處理css呢?我直接引入一個css文件不更方便嗎?
如果只是開發一個簡單的項目當然不使用webpack來開發當然更加方便。
當時一個項目很複雜的話,並且後面如果需要兼顧可維護性和拓展性,很多時候有些css會寫進js文件的,這也是sass和less的出現。
當然webpack也可以將這些一起打包,這裏就不贅敘。
- 圖片寫進js
main.js
var img1 = document.createElement("img");
img1.src = require("./small.png");
document.body.appendChild(img1);
var img2 = document.createElement("img");
img2.src = require("./big.png");
document.body.appendChild(img2);
配置文件webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.(png|jpg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
};
·limit·
是設置loader的,這裏的意思是如果圖片大小超過8192kb,則會變成url,不會解析成js
文件。
這裏的意義可以將一些小圖片合併到一個js文件,這樣請求的時候就可以減少http次數,提高渲染的效率,不過需要考慮到如果js文件太大就會增加白屏時間。
插件PLUGINS
插件目的在於解決 loader 無法實現的其他事。比如壓縮JS文件、新建html文件、設置打開網頁等
- 壓縮JS
配置文件webpack.config.js
var webpack = require('webpack');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new UglifyJsPlugin()
]
};
引入模塊uglifyjs-webpack-plugin
,在plugins直接配置即可。
- 寫入html文件
html-webpack-plugin
能爲你創建index.html
,open-browser-webpack-plugin
能夠在Webpack
加載時打開一個新的瀏覽器標籤(tab)
main.js
document.write('<h1>Hello World</h1>');
配置文件webpack.config.js
var HtmlwebpackPlugin = require('html-webpack-plugin');
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new HtmlwebpackPlugin({
title: 'Webpack-demos',
filename: 'index.html'
}),
new OpenBrowserPlugin({
url: 'http://localhost:8080'
})
]
};
環境變量
我們開發的時候,可以設置一些環境變量。
main.js
document.write('<h1>Hello World</h1>');
if (__DEV__) {
document.write(new Date());
}
配置文件
var webpack = require('webpack');
var devFlagPlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [devFlagPlugin]
};
process.env.DEBUG
這個從何而來?
其實是我們打包的時候可以設置的值,cross-env DEBUG=true
webpack-dev-server --open,這樣DEBUG就可以傳入的給js文件。方便我們做測試模仿用戶輸入的時候使用。
打包的時候,就會自動將對應的變量換爲我們設置的值。
常用loader
-
CSS文件處理相關
style-loader 和 css-loader
使 Webpack 可以處理 import 進來的 css 文件。-
CSS 預處理器: sass-loader和node-sass
-
postcss-loader postcss-preset-env
自動添加css前綴。比如在 Chrome 瀏覽器下使用尚未標準化的新屬性需要添加 -webkit 前綴,在火狐瀏覽器下需要添加 -moz 前綴,在 IE 瀏覽器下需要添加 -ms 前綴。如果爲每個屬性都添加一遍的話,非常繁瑣,效率很低。postcss 通過插件系統,提供了很多強大的功能,比如可以通過 autoprefixer 自動添加瀏覽器前綴,通過 postcss-preset-env 插件,我們可以使用尚未支持的 CSS 特性,插件會幫助我們將未來的 CSS 特性轉換成現有的 CSS,類似於 Babel 將 ES6+ 的語法轉換成 ES5 的語法。
-
-
VUE相關
vue-loader 會解析 .vue 文件,提取每個語言塊,如有必要會通過其它 loader 處理,最後將他們組裝成一個 ES Module,它默認導出一個 Vue.js 組件選項的對象。
vue-template-compiler 會接解析 template 標籤中的內容,預處理爲 JS 渲染函數,並最終注入到從 <script> 導出的組件中。 -
處理資源路徑
url-loader 和 file-loader
我們如果在文件中使用圖片、字體、視頻等文件資源的時候,需要配置對應的 loader 來解析,這裏我們用到的 loader 是
常用plugins
-
html-webpack-plugin
用於自動修改html中的js文件引用自動修改問題。 -
VueLoaderPlugin
將其它規則複製並應用到 .vue 文件裏相應語言的塊中。例如,如果我們有一條匹配 /.js$/ 的規則,那麼它會應用到 .vue 文件裏的 <script>塊。
安裝babel
Babel 是一個轉碼器,負責將 ES2015+ 代碼轉爲 ES5 代碼。有了轉碼器我們可以在開發中使用 ES2015+ 代碼,無需擔心瀏覽器是否已經實現。
npm install --save-dev @babel/core @babel/preset-env babel-loader
npm install --save @babel/polyfill
Babel6.0 以後拆分了幾個獨立的包,並以插件的機制來構築。@babel/core 是 Babel 的核心功能包,必須安裝。
@babel/preset-env 能根據當前的運行環境,自動確定需要的 plugins 和 polyfills。主要負責將代碼轉成 ES5 語法規則。
babel-polyfill。Babel 編譯時只編譯語法,並不會編譯 API 和實例方法,如:async/await、Promise 等,babel-polyfill 會把這些沒有的 API 全部掛載到全局對象,也就是所謂的“墊片”。
babel-loader 是 Webpack 用來轉譯 JS 代碼的加載器。
更新 webpack.config.js:
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
.babelrc
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
},
// corejs的值需與安裝的core-js的版本一致
// 可以通過 npm ls core-js 查看core-js的版本號
"corejs": "3.6.0",
"useBuiltIns": "usage"
}
]
]
}
本地開發環境
自動構建
執行webpack後面加入--watch
就可以自動監視文件,自動構建。
瀏覽器自動刷新
首先安裝 webpack-dev-server
在 Webpack 的配置文件中配置:
module.exports = {
// ...省略其他配置
devServer: {
contentBase: path.join(__dirname, "dist")
}
}
在 package.json 中定義 npm script。具體如下:
"scripts": {
"start": "webpack-dev-server --open"
},
熱模塊
devServer: {
contentBase: path.join(__dirname, "dist"),
hot:true
}
sourceMap
定位錯誤在源文件
sourceMap 有多種選項,開發者可以根據不同的需求進行選擇。我們來看一下幾種不同的選項之間的對比:
devtool | 構建速度 | 重新構建速度 | 適用環境 | 精準度 |
---|---|---|---|---|
none | +++ | +++ | 生產環境 | 不生成 source map |
source-map | -- | -- | 生產環境 | 映射到原始源代碼,source map 作爲單獨的文件保存 |
inline-source-map | -- | -- | 開發環境 | 映射到原始源代碼,source map 轉換爲 DataUrl 後添加到 bundle 中,會導致文件大小劇增。 |
eval | +++ | +++ | 開發環境 | 映射到轉換後的代碼,而不是源代碼,行數映射不正確 |
eval-source-map | -- | + | 開發環境 | 映射到原始源代碼,只映射到行。 |
根據以上信息我們可以得出基本的結論:
生產環境中不生成 sourcemap,或者如果需要擁有錯誤上報工具,選擇 source-map。
開發環境中,根據對代碼映射精確度的要求,可以選擇 eval, eval-source-map,inline-source-map。
搭建本地 Mock 服務
前後端分離的協作模式下,前後端之間的數據傳輸都是基於 HTTP 接口實現。顯然前端的開發是依賴後端接口實現的。在講求快速迭代的互聯網公司這種串行的等待肯定是不會出現的。前後端約定好接口的出參和入參之後,前端數據模擬(mock)就很有必要了。前端基於接口文檔自己模擬一份假數據作爲前端的數據源。後端按照約定的文檔實現接口功能。待接口完全實現之後,前後端切換到真實的接口進行聯調。顯然,mock 對於開發效率的提升是非常重要的。
代碼檢查
-
ESLint
npm install -D eslint eslint-loader eslint-plugin-vue babel-eslint eslint-friendly-formatter
eslint-loader 負責對 JS 的源代碼執行靜態檢查。
用 Vue 官方開發的 ESLint 插件 eslint-plugin-vue 對 .vue 文件進行檢查。
使用 eslint-friendly-formatter 來美化代碼檢查報告
需要配置eslint文件.eslintrc.js
module.exports = { env: { browser: true, node: true, es6: true }, extends: ["eslint:recommended", "plugin:vue/essential"], parserOptions: { parser: "babel-eslint", sourceType: "module" }, plugins: ["vue"], rules: {} };
-
CSS檢查器
npm install -D stylelint stylelint-webpack-plugin
首先將 stylelint-webpack-plugin 引入,然後將其添加到 plugins 中。StyleLintPlugin 接收一個配置對象作爲參數,files 屬性使用 glob 規則匹配 stylelint 需要進行檢查的文件。這裏我們檢查 src 目錄下的所有以 Vue、CSS、SCSS、Sass 結尾的文件。
const StyleLintPlugin = require("stylelint-webpack-plugin"); module.exports = { // ...省略其他配置 plugins: [ new StyleLintPlugin({ files: ["src/**/*.{vue,css,scss,sass}"] }) ] };
與 ESLint 一樣,stylelint 所有的規則默認都是關閉的。我們需要在 stylelint 的配置文件中啓用希望使用的規則。
在根目錄創建.stylelintrc.js 文件,添加如下內容:
module.exports = { rules: { "color-no-invalid-hex": true, "color-hex-case": "lower", "unit-whitelist": ["em", "rem", "%", "s", "px"] } };
我們定義了 3 條規則。不允許使用非法的十六進制顏色值,不允許顏色值大寫,允許使用的度量單位是 em、rem、%、s、px。