ebpack 模塊化的配置,包括處理 js轉換 / css / html / eslint / 圖片等的 loader、plugin 配置。 傳說中前端配置工程師的基本修養……
模塊是為了完成某種功能所需的程序或子程序,模塊是系統中職責單一且可替換的部分。
模塊化就是把系統代碼分為一系列職責單一且可替換的模塊。模塊化開發是指如何開發新的模塊和復用已有的模塊來實現應用的功能。
javascript 的三大主流模塊規范已在上篇介紹過,這么不多贅述。
Webpack 中一切皆模塊,包括 javascript、css、html、圖片、字體、富媒體及 less、sass、 各種 javascript 庫等等,webpack 可以針對不同的模塊用不同的 loader 做不同的解析編譯,做到按需加載。比如對于 less 代碼,可以先用 less-loader 轉換為 css 代碼,用 css-loader 處理其中的 url/import 等邏輯,最后用 style-loader 把 css 插入到 <style> 標簽里。
Webpack 導入模塊的方式不限于三大主流模塊規范,提供了 import() 引入模塊的方式。
與 import from 不同的是, import from 是靜態分析打包的,而 import() 是動態分析打包的,通過異步的方式加載模塊,import() 返回一個 Promise 對象。并且 import() 被視為分割點,被請求的模塊和它引用的所有子模塊,打包后會分割到一個單獨的 chunk 中。
// 想象我們有一個從 cookies 或其他存儲中獲取語言的方法
const language=detectVisitorLanguage();
import(`./locale/${language}.json`).then((module)=> {
// do something with the translations
});
神奇注釋(Magic Comments),是指可以通過在 import() 中添加注釋實現對打包的文件做一定的操作,比如給 chunk 命名、選擇不同模式。
// 單個目標
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackExports: ["default", "named"] */
'module'
);
// 多個可能的目標
import(
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackPrefetch: true */
/* webpackPreload: true */
`./locale/${language}`
);
Magic Comments | 說明 |
webpackChunkName | chunk 文件的名稱 |
webpackInclude | 在導入解析過程中,用于匹配的正則表達式,只有匹配到的模塊才會被打包 |
webpackExclude | 用于匹配的正則表達式,匹配到的模塊不會被打包 |
webpackMode | 指定以不同的模式解析動態導入,可選值lazy/lazy-once/eager/weak |
webpackPrefetch | 是否預取模塊,及其優先級 |
webpackPreload | 是否預加載模塊,及其優先級 |
通過神奇注釋,import()不再是簡單的 JavaScript 異步加載器,還是任意模塊資源的加載器,舉例說明:如果我們頁面用到的圖片都放在src/assets/img文件夾下,你們可以通過下面方式將用到的圖片打包到一起:
import(/* webpackChunkName: "image", webpackInclude: /\.(png|jpg|gif)/ */ './assets/img');
require.context()
require.context(directory, includeSubdirs, filter)可以批量將 directory內的文件全部引入進文件,并且返回一個具有resolve的 context 對象,使用context.resolve(moduleId)則返回對應的模塊。該功能十分有用。
require.context() 會將所有的文件都引入進 bundle。
require.resolve()
require.resolve(dependency: String)可以獲取模塊的唯一 ID, 并把模塊真實引入 bundle。
require.include()
require.include(dependency: String)引入一個不需要執行的 dependency,這樣可以用于優化輸出 chunk 中依賴模塊的位置。
/**
* 輸出:
* entry chunk: file.js and a
* anonymous chunk: b
* anonymous chunk: c
* 不使用 require.include('a'),輸出的兩個匿名 chunk 都會有模塊 a
*/
require.include('a');
require.ensure(['a', 'b'], function (require) {
/* ... */
});
require.ensure(['a', 'c'], function (require) {
/* ... */
});
在 webpack 中編寫 JavaScript 代碼,可以使用最新的 ES 語法,而最終打包的時候,webpack 會借助 Babel 將 ES6+語法轉換成在目標瀏覽器可執行 ES5 語法。
Babel 擁有自己的 cli,可以單獨安裝使用,具體參考 Babel 官網,這里不贅述。
在 npm 項目中,Babel 支持兩種文件配置方式:
// package.json
{
"name": "my-package",
"version": "1.0.0",
"babel": {
"presets": ["@babel/preset-env"]
}
}
// .babelrc
{
"presets": ["@babel/preset-env"]
}
如果使用了webpack,也可以配置在 webpack 的配置文件中:
// 安裝開發依賴
npm i webpack babel-loader webpack-cli @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
// 將 runtime 作為依賴
npm i @babel/runtime -S
// 配置示例
module.exports={
entry: './babel.js',
mode: 'development',
devtool: false,
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage'
}
]
]
}
}
]
}
]
}
};
配置 env 選項可以實現在不同環境下使用不同的 Babel 配置。
env 選項的值將從 process.env.BABEL_ENV 獲取,如果沒有的話,則獲取 process.env.NODE_ENV 的值,如果這個也沒有,會默認設置為 "development"。
{
"env": {
"production": {
"presets": ["@babel/preset-env"]
}
}
}
polyfill:在 JavaScript 中表示一些可以抹平瀏覽器實現差異的代碼,比如某瀏覽器不支持 Promise,可以引入es6-promise-polyfill 等庫來解決。
Babel 的插件分為兩類:轉換插件和語法解析插件。轉換插件用于對語法進行轉換,如 ES6 轉換為 ES5,語法解析插件用來擴展語法,如解析 React 特殊設計的jsx語法。對于各種情況下的語法轉換與解析,需要一個一個的引入相應的插件,這種方式比較麻煩,一般場景下,可以直接使用官方推出的插件組合@babel/preset-env。
@babel/preset-env可以根據開發者的配置按需加載對應的插件,還可以根據代碼執行平臺環境和具體瀏覽器版本生成相應的 js 代碼。
@babel/preset-env有兩個重要的配置項:useBuiltIns和target。
/**
* useBuiltIns: usage|entry|false,一般采用 usage 這種方式,即指定按需加載。
* target: 指定構建目標
*/
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage|entry|false"
"targets": {
// "browsers": "IE 10" // 指定IE 10瀏覽器
"node": "8.9.3" // 如果在node環境中,指定node版本號
}
}]
]
}
Browserslist 目標瀏覽器的設置
項目的實際開發與運行中,很多情況下要做一定的瀏覽器兼容,而 Browserslist 就是用來設置目標瀏覽器的工具。許多開發工具包括 npm、babel 都兼容內置了 Browserslist。
Browserslist 的配置可以放在 package.json 中,也可以單獨放在配置文件.browserslistrc 中。相關的開發工具都會主動查找 browserslist 的配置文件,根據 browserslist 配置找出對應的目標瀏覽器集合。
// package.json 內配置
{
"browserslist": ["last 2 version", "> 1%", "maintained node versions", "not ie < 11"]
}
// 兼顧不同環境的配置,設置BROWSERSLIST_ENV 或者 NODE_ENV可以配置不同的環境變量
{
"browserslist": {
"production": ["> 1%", "ie 10"],
"development": ["last 1 chrome version", "last 1 firefox version"]
}
}
// 項目的根目錄下.browerslistrc文件:
// 注釋是這樣寫的,以#號開頭
// 每行一個瀏覽器集合描述
last 2 version
> 1%
maintained node versions
not ie < 11
// 兼顧不同環境的配置
[production staging]
> 1%
ie 10
[development]
last 1 chrome version
last 1 firefox version
常見的瀏覽器集合范圍說明:
范圍 | 說明 |
last 2 versions | caniuse.com網站跟蹤的最新兩個版本,假如 iOS 12 是最新版本,那么向后兼容兩個版本就是 iOS 11 和 iOS 12 |
> 1% | 全球超過 1%人使用的瀏覽器,類似> 5% in US則指代美國 5%以上用戶 |
cover 99.5% | 覆蓋 99.5%主流瀏覽器 |
chrome > 50 ie 6-8 | 指定某個瀏覽器版本范圍 |
unreleased versions | 說有瀏覽器的 beta 版本 |
not ie < 11 | 排除 ie11 以下版本不兼容 |
since 2013 last 2 years | 某時間范圍發布的所有瀏覽器版本 |
maintained node versions | 所有被 node 基金會維護的 node 版本 |
current node | 當前環境的 node 版本 |
dead | 通過last 2 versions篩選的瀏覽器中,全球使用率低于0.5%且官方聲明不在維護或者事實上已經兩年沒有再更新的版本 |
defaults | 默認配置,> 0.5% last 2 versions Firefox ESR not dead |
瀏覽器名稱列表(大小寫不敏感):
瀏覽器 | 說明 | 瀏覽器 | 說明 |
Chrome | chrome瀏覽器 | ChromeAndroid/and_chr | chrome 安卓移動瀏覽器 |
Edge | Edge瀏覽器 | Explorer/ie | ie瀏覽器 |
Android | 安卓webview瀏覽器 | Baidu | 百度瀏覽器 |
Firefox/ff | 火狐瀏覽器 | FirefoxAndroid/and_ff | 火狐安卓瀏覽器 |
iOS/ios_saf | iOS Safari 瀏覽器 | ExplorerMobile/ie_mob | ie 移動瀏覽器 |
Safari | 桌面版本 Safari | Node | nodejs |
Opera | opera瀏覽器 | OperaMobile/op_mob | opera 移動瀏覽器 |
OperaMini/op_mini | operaMini 瀏覽器 | QQAndroid/and_qq | QQ 安卓瀏覽器 |
Samsung | 三星瀏覽器 | UCAndroid/and_uc | UC 安卓瀏覽器 |
Electron | Electron | BlackBerry / bb | 黑莓瀏覽器 |
Babel Polyfill 的最佳實踐:
useBuiltIns:'usage'可以近乎完美的解決我們的 Polyfill 問題,它是按需引入模塊,根據 .browserslist + 業務實際代碼來設置引入 Polyfill,不會多余的引入。
Webpack 中一切皆模塊,經 loader 處理后的 CSS 可以在 js 中被直接引用。
// 引入 css-loader
npm install --save-dev css-loader
// webpack.config.js 配置loader
{
module: {
rules: [
{
test: /\.css$/,
use: ['css-loader']
}
];
}
}
// 代碼中引入 css 模塊
import css from './css/index.css';
console.log(css);
// 或者不用webpack配置文件,直接在引入語句中使用loader
import css from 'css-loader!./css/index.css';
console.log(css);
經 css-loader 處理后,css 模塊被打包轉換成 js 對象,可直接用 js 語法使用。
style-loader 的作用是將 css-loader 打包好的 css 代碼以<style>標簽的形式插入到 html 文件中,一般情況下,在一個項目中 style-loader 與 css-loader 是成對出現的,并且 style-loader 要在 css-loader 之后。
npm install --save-dev style-loader
CSS 作為<style>標簽放到 HTML 內還是不夠的,我們還需要將 CSS 以 <link> 的方式通過 URL 的方式引入進來,這時候就需要使用 mini-css-extract-plugin 。
npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin=require('mini-css-extract-plugin');
module.exports={
plugins: [
// 添加 plugin
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css'
})
],
module: {
rules: [
{
test: /\.css$/,
// 添加 loader
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
}
};
CSS Modules 指的是所有的 CSS 類名及其動畫名都只是局部作用域的 CSS 文件。CSS Modules 主要解決的問題有:
CSS Module 的表現形式:
/* app.css */
.element {
background-color: blue;
color: white;
font-size: 24px;
}
/* app.js */
// 引入 app.css
import styles from './app.css';
// js 直接使用 CSS 的類名作為對象值
let element=`
<div class="${styles.element}">
<p>CSS Modules</p>
</div>
`;
document.write(element);
CSS Modules 在 webpack.config.js 中的配置:
// CSS Modules 相關的配置還有很多,可以查看css-loader對應的文檔。
module.exports={
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}
};
常見的 CSS 預處理器有 Less、Sass 及其語法變種 Scss和Stylus。預處理器為 CSS 提供了變量、函數、運算、作用域、繼承、嵌套等語法,使其功能更加強大。
以 less-loader 為例,less-loader 將 Less 語法編譯成 CSS,后續還需要使用 css-loader 和 style-loader 處理才可以,所以一般來說需要配合使用:
module.exports={
module: {
rules: [
{
test: /\.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true
}
},
'less-loader' // 將 Less 編譯為 CSS
]
}
]
}
};
一些預處理語言需要安裝對應的解析器,例如 sass-loader,需要同時安裝 node-sass。
npm install sass-loader node-sass --save-dev
表現形式:
/*沒有前綴的寫法*/
.flex {
display: flex;
}
/*經過 postcss autoprefixer 處理后*/
.flex {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
PostCss 能實現的功能很多,除了添加前綴,還可以最新語法轉義、壓縮等,甚至可以擴展 CSS 的語言特性。配置了 postcss-loader 之后,WebPack 就可以使用 PostCSS 來處理 CSS了,但是 PostCSS 本身只不過是將 CSS 解析成 AST ,真正起作用的還需要依賴其強大的插件系統。
PostCSS 配置其實主要是配置其使用哪些插件,PostCSS 的配置寫法有以下三種方式:
/* postcss.config.js */
// 引入postcss 插件
const autoprefixer=require('autoprefixer');
module.exports={
plugins: [autoprefixer(['IE 10'])]
};
/* webpack.config.js */
// 引入postcss 插件
const autoprefixer=require('autoprefixer');
module.exports={
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
// 通過 plugins 選項
plugins: [autoprefixer(['IE 10'])]
}
}
]
}
]
}
};
/* package.json:受限于 json 的語法,可擴展性較弱,一般不推薦 */
{
"postcss": {
"plugins": {
"autoprefixer": "IE 10"
}
}
}
以下羅列幾種常用的 PostCSS 插件,詳細用法參見各自的官網或相關文檔。
Autoprefixer
給 css 補齊各種瀏覽器私有的前綴,例如 -webkit、-moz、-ms 等,當然還會處理各種兼容性問題,比如 flex 語法,不能簡單添加 -webkit 就解決,還需要處理成 -webkit-box 這類老版本的標準。
Autoprefixer 的主要參數就是 browserslist。
postcss-preset-env
postcss-preset-env 是跟 babel 的 preset-env 類似的功能,不用一一引入要用的插件,在打包構建的時候,會根據不同的配置輸出對應支持的 CSS 文件。
PreCSS
可以寫類似 Sass 和 cssnext 語法的 CSS。
cssnano
CSS 壓縮優化。
npm install eslint --save-dev
安裝之后,可以通過使用 npx eslint 直接運行,在運行之前,我們需要創建個 ESLint 的配置文件,使用 eslint --init 命令創建 .eslintrc.json 文件。
.eslintrc.json 內容示例,具體可查看 eslint 官網文檔:
{
"env": {
"browser": true,
"es6": true
},
"extends": "airbnb-base",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {
// 禁止 console,要用寫 eslint disbale
'no-console': 2,
// 禁止 debugger,防止上線
'no-debugger': 2,
// 禁止 alert,要用寫 eslint disable
'no-alert': 2,
// 不用的 var,要刪除,手動 tree shaking,要潔癖
'no-unused-vars': 2,
// 沒定義就用的就別用,全局的要用 寫 eslint global
'no-undef': 2
}
}
ESLint 的報錯類型包括三種:off、warn和error,分別對應著:0、1、2,所以上面的配置的 rule 實際為error級別的規則,檢測到了則直接報錯誤(Error)。
webpack 中的配置:
npm install eslint-loader --save-dev
module.exports={
module: {
rules: [
{
test: /\.js$/,
loader: 'eslint-loader',
enforce: 'pre', // 調整了 loader 加載順序,保證先檢測代碼風格,之后再做 Babel 轉換等工作
include: [path.resolve(__dirname, 'src')], // 指定檢查的目錄
options: { // 這里的配置項參數將會被傳遞到 eslint 的 CLIEngine
formatter: require('eslint-friendly-formatter') // 指定錯誤報告的格式規范
}
}
]
}
};
StyleLint 的配置文件是.stylelintrc.json,其中的寫法跟 ESLint 的配置類似
npm install -D stylelint
/* .stylelintrc.json */
{
"extends": ["stylelint-config-standard", "stylelint-config-recess-order"],
"rules": {
"at-rule-no-unknown": [true, {"ignoreAtRules": ["mixin", "extend", "content"]}]
}
}
webpack 中配置:
const StyleLintPlugin=require('stylelint-webpack-plugin');
module.exports={
// ...
plugins: [new StyleLintPlugin(options)]
// ...
};
默認 StyleLint-webpack-plugin 會查找項目中的 StyleLint 配置文件,根據配置文件的配置來檢測 CSS 代碼。
前端的靜態資源一般指圖片、字體文件、富媒體等。
圖片引入的一般方式有,Html 引入,Css 引入和 Javascript 的引入。在 webpack 中可以使用 loader 來處理加載圖片。
file-loader 與 url-loader
這是最常見的處理圖片的兩種 loader。它倆區別在于:
webpack 的配置如下,使用 publicPath 可做靜態資源的 CDN 處理,可以使用 resolve.alias 做別名處理:
// webpack.config.js
const HTMLPlugin=require('html-webpack-plugin');
module.exports={
mode: 'development',
entry: './src/index.js',
// output: {
// publicPath: 'http://bd.bxstatic.com/img/'
// }
devtool: false,
module: {
// resolve: {
// alias: {
// '@assets': path.resolve(__dirname, './src/assets')
// }
// },
rules: [
{
test: /\.html$/,
use: ['html-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
// url-loader 默認會做 Base64 處理,這里限制不超過 3k 大小的圖片才做 Base64 處理
limit: 3*1024
}
}
}
]
},
plugins: [
new HTMLPlugin({
template: './src/index.html'
})
]
};
在 Html 和 Css 中用別名的方式引入圖片時,要在別名前加~符號:
<img src="~@assets/img/large.png" alt="背景圖" />
.bg-img {
background: url(~@assets/img/small.png) no-repeat;
}
svg-url-loader
svg-url-loader 的工作原理類似于 url-loader,主要用來處理 SVG 文件。 svg-url-loader 擁有改善 IE 瀏覽器支持的選項,但是在其他瀏覽器中很糟糕。如果你需要兼容 IE 瀏覽器,設置 iesafe: true選項。
module.exports={
module: {
rules: [
{
test: /\.svg$/,
loader: 'svg-url-loader',
options: {
// 小于 10kB(10240字節)的內聯文件
limit: 10 * 1024,
// 移除 url 中的引號
// (在大多數情況下它們都不是必要的)
noquotes: true
}
}
]
}
};
img-webpack-loader
img-webpack-loader 用來對圖片進行壓縮優化,支持 JPG、PNG、GIF 和 SVG 格式的圖片,不過必須和 url-loader 以及 svg-url-loader 一起使用。
module.exports={
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/,
loader: 'image-webpack-loader',
//提高優先級,保證在url-loader和svg-url-loader之前完成圖片優化
enforce: 'pre'
}
]
}
};
postcss-sprites
將小圖標合成雪碧圖。需要結合 postcss-loader 使用。
// postcss.config.js
const postcssSprites=require('postcss-sprites');
module.exports={
plugins: [
postcssSprites({
// 在這里制定了從哪里加載的圖片被主動使用css sprite
// 可以約定好一個目錄名稱規范,防止全部圖片都被處理
spritePath: './src/assets/img/'
})
]
};
//webpack.config.js
module.exports={
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader'
}
]
}
]
}
};
可以直接使用 url-loader 或者 file-loader 進行配置即可,不需要額外的操作。如果不需要 Base64,那么可以直接使用 file-loader,需要的話就是用 url-loader。
{
// 文件解析
test: /\.(eot|woff|ttf|woff2|appcache|mp4|pdf)(\?|$)/,
loader: 'file-loader',
query: {
// 這么多文件,ext不同,所以需要使用[ext]
name: 'assets/[name].[hash:7].[ext]'
}
}
類似 CSV、TSV 和 XML 等數據,那么我們需要單獨給它們配置相應的 loader。內置支持 JSON 數據。
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
{
test: /\.xml$/,
use: [
'xml-loader'
]
}
webpack 處理 html 頁面,一般是使用 html-webpack-plugin 插件。
// 安裝
npm install html-webpack-plugin --save-dev
簡單配置示例
const HtmlWebPackPlugin=require('html-webpack-plugin');
module.exports={
mode: 'development',
entry: {
main: './src/index.js'
},
plugins: [new HtmlWebPackPlugin()]
// 指定打包后頁面的名稱(默認index.html)和 title
// plugins: [new HtmlWebPackPlugin({title: 'hello', filename: 'foo.html'})]
};
打包后會生成 index.html,內容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Webpack App</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<script src="main.js"></script>
</body>
</html>
指定Template打包
指定一個 template,使打包后的 html 頁面是依據這個 template 來打包的,這樣 html 的內容我們可以自定義,比如我們需要打包后的文件有<div id="app"></div>節點。
指定 template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Webpack</title>
</head>
<body>
<h1>hello world</h1>
<div id="app"></div>
</body>
</html>
webpack.config.js 配置:
const HtmlWebPackPlugin=require('html-webpack-plugin');
module.exports={
mode: 'development',
entry: {
main: './src/index.js'
},
plugins: [
new HtmlWebPackPlugin({
template: './src/index.html' // 指定模板
})
]
};
打包后的 html 文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack</title>
</head>
<body>
<h1>hello world</h1>
<div id="app"></div>
<script src="main.js"></script></body>
</html>
多頁配置
單一入口:
const HtmlWebPackPlugin=require('html-webpack-plugin');
const indexPage=new HtmlWebPackPlugin({
template: './src/index.html',
filename: 'index.html'
});
const listPage=new HtmlWebPackPlugin({
template: './src/list.html',
filename: 'list.html'
});
module.exports={
mode: 'development',
entry: {
main: './src/index.js'
},
plugins: [indexPage, listPage]
};
多入口:
const HtmlWebPackPlugin=require('html-webpack-plugin');
module.exports={
mode: 'development',
entry: {
index: './src/index.js',
list: './src/list.js'
},
plugins: [
new HtmlWebPackPlugin({template: './src/index.html', filename: 'index.html', chunks: ['index']}),
new HtmlWebPackPlugin({template: './src/list.html', filename: 'list.html', chunks: ['list']})
]
};
最佳實踐:
/**
* 1. /scripts/utils/index.js
* 使用 npm 庫 globby 讀取src/pages/*.js, 生成入口 entry
*/
const path=require('path');
const globby=require('globby');
const getEntry=(exports.getEntry=()=> {
// 異步方式獲取所有的路徑
const paths=globby.sync('./pages/*.js', {
cwd: path.join(__dirname, './src')
});
const rs={};
paths.forEach(v=> {
// 計算 filename
const name=path.basename(v, '.js');
let p=path.join('./src', v);
if (!p.startsWith('.')) {
// 轉成相對地址
p='./' + p;
}
rs[name]=p;
});
return rs;
});
console.log(getEntry());
/**
* 2. /scripts/utils/index.js
* 遍歷 entry,生成 html-webpack-plugins 數組
*/
const HtmlWebPackPlugin=require('html-webpack-plugin');
exports.getHtmlWebpackPlugins=()=> {
const entries=getEntry();
return Object.keys(entries).reduce((plugins, filename)=> {
plugins.push(
new HtmlWebPackPlugin({
template: entries[filename],
filename: `${filename}.html`,
chunks: [filename]
})
);
return plugins;
}, []);
};
/**
* 3. webpack.config.js
* require 封裝好的html-webpack-plugins 數組
*/
const {getEntry, getHtmlWebpackPlugins}=require('./scripts/utils');
module.exports={
mode: 'development',
getEntry(),
plugins: [
...getHtmlWebpackPlugins()
]
};
webpack-dev-server 是一個可以用來啟動本地服務的插件。通過命令啟動對應的服務。
// 安裝
npm install webpack-dev-server
// 啟動命令
npx webpack-dev-server
/* 命令常用參數 */
// 修改端口號和 host
webpack-dev-server --port 3000 --host 127.0.0.1
// 啟動inline 模式的自動刷新
webpack-dev-server --hot --inline
// 手動指定 webpack config 文件
webpack-dev-server --config webpack.xxx.js
// 指定 webpack 的 mode
webpack-dev-server --mode development
// watch 功能,文件發生變化則觸發重新編譯
webpack-dev-server --watch
// dev-server默認會將工作目錄(當前目錄)作為基本目錄,可以手動修改它
webpack-dev-server --content-base ./build
執行 webpack-dev-server 命令之后,它會讀取 Webpack 的配置文件(默認是 webpack.config.js),然后將文件打包到內存中(所以看不到dist文件夾的生產,Webpack 會打包到硬盤上),這時候打開 server 的默認地址:localhost:8080 就可以看到文件目錄或者頁面(默認是顯示 index.html,沒有則顯示目錄)。
一般在項目開發中,啟動本地服務的命令會配置在 package.json 的 scripts 屬性中:
{
"scripts": {
"dev": "webpack-dev-server --mode development --config webpack.config.dev.js --hot --inline --port 3000"
}
}
更多的時候,是通過 devServer 屬性直接配置在 webpack.config.js 中:
const path=require('path');
module.exports={
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: 9000, // 端口
hot: true, // 熱部署,開啟熱部署需要引入 HotModuleReplacementPlugin 插件
inline: true,
// proxy解決本地開發跨域的問題,/api所有請求都轉發到了baidu.com
proxy: {
'/api': 'http://baidu.com'
// secure: false // 支持 https
}
// 加載所有內部中間件之前和之后可以插入自定義的中間件
before(app, server) {
app.get('/api/mock.json', (req, res)=> {
res.json({hello: 'world'});
});
},
after(){}
},
plugins: [
// 添加 hmr plugin
new webpack.HotModuleReplacementPlugin()
]
};
HMR 即模塊熱替換(Hot Module Replacement)的簡稱,它可以在應用運行的時候,不需要刷新頁面,就可以直接替換、增刪模塊。
開發環境和生產環境的配置可能不同,需要分開、判斷處理。
開發 vue 一般通過集成了 webpack 的 vue-cli 創建項目,但如果直接使用 webpack 需要引入 vue-loader。
作者:越谷
鏈接:https://juejin.cn/post/7380262523852455963
來源:稀土掘金
TOC
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
css<br />body {<br /> background-color:red; <br />}<br />
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
@fontSize: 50px; @fontColor: orange; body{ font-size: @fontSize; color: @fontColor; }
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
注:可以發現,對于大的圖片打包成功會輸出到dist文件夾下,此時引用的話會找不到,需要配置下路徑
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
這樣就會實現,當文件改變的時候,頁面會自動修改,因為此時這些修改保存到了內存中,類似于java的熱部署
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
注:以上安裝的模塊的版本要對應!!!
學習視頻: https://www.bilibili.com/video/BV15741177Eh?p=90&spm_id_from=pageDriver
關概念
前端模塊管理器(package management)
Web應用的目前的開發是各個模塊組合起來(如:加載各種js插件),為了使用各個模塊,需要使用大量的script標簽引進模塊,這樣就造成了Web頁面比較臃腫,同時在瀏覽器調用時也需要發送大量的HTTP請求.
為了解決這一問題,那么就需要使用前端模塊管理器來管理JavaScript腳本的依賴關系,自動加載各個模塊,使得網頁結構清晰合理.
常用的前端模塊管理器:Bower,Browserify,Component,Duo
WebPack
安裝使用
使用說明
Switch to the directory containing webpack.config.js and run:
ebpack
for building once for development
ebpack -p
for building once for production (minification)
ebpack --watch
for continuous incremental build in development (fast!)
webpack -d
to include source maps
簡單使用示例
項目初始化和模塊安裝
npm init -ynpm install jquery --save
npm install webpack --save-dev
在項目根目錄下創建webpack的配置文件webpack.config.js
var webpack=require('webpack');var commonsPlugin=new webpack.optimize.CommonsChunkPlugin('common.js');
module.exports={
//插件項
plugins: [commonsPlugin],
//頁面入口文件配置
entry: './src',
//入口文件輸出配置
output: {
path: 'builds',
filename: 'bundle.js'
},
};
相關代碼文件
根目錄下創建src和builds
app的入口文件(ES5語法):
src/index.js
var $=require('jquery');$('body').html('Hello');
項目根目錄下創建index.html
<!DOCTYPE html><html>
<head></head>
<body>
<h1>My title</h1>
<a>Click me</a>
<script src="builds/common.js"></script>
<script src="builds/bundle.js"></script>
</body>
</html>
運行WebPack命令編譯bundle.js
執行命令node_modules\.bin\webpack
后瀏覽器查看效果
將Bootstrap示例代碼使用WebPack打包
安裝所需的加載器
npm install css-loader node-sass resolve-url-loader sass-loader style-loader url-loader file-loader --save
解決jQuery全局問題
npm install jquery --save-dev
WebPack配置文件
var webpack=require('webpack');
var commonsPlugin=new webpack.optimize.CommonsChunkPlugin('common.js');
module.exports={
//插件項
plugins: [commonsPlugin],
//頁面入口文件配置
entry: './src/main.js',
//入口文件輸出配置
output: {
path: 'builds',
filename: 'bundle.js'
},
module: {
//加載器配置
loaders: [
{
// 得到jquery模塊的絕對路徑
test: require.resolve('jquery'),
// 將jquery綁定為window.jQuery
loader: 'expose?jQuery'
},
// 匹配各種資源文件
{
test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
loader: 'url-loader?limit=50000&name=[path][name].[ext]'
},
{
test: /\.js/,
loader: 'babel',
},
{
test: /\.css$/,
loader: 'style!css'
},
{
test: /\.html/,
loader: 'html',
}
]
},
plugins:[
new webpack.ProvidePlugin({
// jQuery設置為全局變量
$:"jquery",
jQuery:"jquery",
"window.jQuery":"jquery"
})
]
};
入口文件main.js
// 加載css資源
require('./css/bootstrap.min.css');
require('./css/bootstrap-theme.min.css');
require('./css/theme.css');
// 加載其他js資源
//require('jquery');
require('./js/bootstrap.min.js');
require('./js/docs.min.js');
require('./js/ie10-viewport-bug-workaround.js');
這里是使用打包bootstrap源碼的方式,也可以使用npm install bootstrap --save
的方式加載bootstrap
打包運行與原生bootstrap的比較網絡傳輸速度
測試效果圖
注意:
測試前為了防止瀏覽器緩存需要清除瀏覽器緩存
如果靜態資源文件有些會經常變化,可采用分文件打包的方式進行打包
參考文章
前端模塊管理器簡介
webpack入門——webpack功能集合的demo
基于webpack搭建前端工程解決方案探索
webpack中引用jquery的簡單實現
*請認真填寫需求信息,我們會在24小時內與您取得聯系。