webpack都更新到4.17.x了,官網(wǎng)的文檔有一部分還停留在4.x以下,因此寫(xiě)下這篇文章,以便后續(xù)遇到坑的時(shí)候再次抓耳撓腮。
項(xiàng)目地址: webpack4-skeleton
目錄結(jié)構(gòu)
├── dist 打包輸出目錄
├── node_modules npm安裝的依賴(lài)包目錄
├── public 靜態(tài)資源目錄
│ ├── static 第三方靜態(tài)文件目錄
│ │ └── images 靜態(tài)圖片目錄
│ ├── favicon.ico 網(wǎng)頁(yè)圖標(biāo)
│ └── index.html 入口html
├── src 開(kāi)發(fā)源代碼目錄
│ ├── assets 靜態(tài)文件目錄
│ │ ├── css 樣式文件目錄
│ │ ├── fonts 字體目錄
│ │ ├── img 靜態(tài)圖片目錄
│ │ └── js 公共函數(shù)目錄
│ ├── components 可以復(fù)用的模塊目錄
│ ├── pages 頁(yè)面存放目錄
│ └── index.js 入口js
├── webpack-config webpack配置目錄
│ ├── common.js 配置文件入口
│ ├── dev.js 開(kāi)發(fā)環(huán)境配置
│ └── prod.js 生產(chǎn)環(huán)境配置
├── .eslintignore ESlint忽略文件配置信息
├── .eslintrc.js ESlint配置信息
├── babel.config.js babel配置信息
└── package.json 項(xiàng)目配置信息
初始化項(xiàng)目
npm init
按照提示配置項(xiàng)目的基礎(chǔ)信息后,即可在項(xiàng)目文件夾下到生成了package.json文件。
用ESlint檢查語(yǔ)法規(guī)范
首先安裝必要的package
npm i -D eslint eslint-config-airbnb-base babel-eslint eslint-loader eslint-plugin-html eslint-plugin-import
由于airbnb的規(guī)范過(guò)于嚴(yán)格,創(chuàng)建.eslintrc.js,創(chuàng)建自定義配置文件。
module.exports = {
extends: "airbnb-base", // 繼承airbnb的語(yǔ)法檢測(cè)規(guī)則
root: true,
env: {
node: true,
browser: true, // 檢測(cè)環(huán)境基于瀏覽器,避免在調(diào)用window對(duì)象的時(shí)候報(bào)錯(cuò)
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'strict': 0,
'no-param-reassign': ["error", {
"props": false, // 允許給函數(shù)的參數(shù)重新賦值
}],
'max-len': ["error", {
"code": 240, // 允許單行代碼最長(zhǎng)為240個(gè)字符
}],
'no-plusplus': ["error", {
"allowForLoopAfterthoughts": true, // 允許在for循環(huán)中用 i++寫(xiě)法
}],
'no-new': 'off', // 允許new Object()
'class-methods-use-this': 'off', // 允許class中的function內(nèi)不使用this
'import/no-unresolved': 0, // 避免webpack指定alias,后eslint報(bào)錯(cuò)
},
plugins: [
'html'
],
parser: 'babel-eslint'
};
接下來(lái)創(chuàng)建.eslintignore,忽略開(kāi)發(fā)時(shí)源代碼之外的文件。
/node_modules/
/webpack-config/
/dist/
/public/
/static/
/*.js
安裝webpack、babel和必要的webpack loader及webpack plugin
npm i -D webpack webpack-cli webpack-dev-server "@babel/core" "@babel/plugin-syntax-dynamic-import" "@babel/plugin-transform-async-to-generator" "@babel/plugin-transform-regenerator" "@babel/plugin-transform-runtime" "@babel/preset-env" "@babel/runtime" babel-loader css-loader file-loader html-loader@next html-webpack-plugin@next less less-loader regenerator-runtime style-loader url-loader
html-webpack-plugin@next 安裝最新的alpha版本是為了解決以下bug:
Fail to inject code-splitting script files when webpack 4 optimization.splitChunks.name: false
html-loader@next 安裝最新的alpha版本是因?yàn)閣ebpack 4.x已經(jīng)禁用了html-loader的htmlLoader選項(xiàng),安裝alpha版可以Disable HTML Assets。
配置webpack的開(kāi)發(fā)環(huán)境
首先安裝必要的package
npm i -D copy-webpack-plugin friendly-errors-webpack-plugin webpack-merge
-
copy-webpack-plugin用來(lái)拷貝靜態(tài)資源 -
friendly-errors-webpack-plugin能夠更好在終端看到webapck運(yùn)行的警告和錯(cuò)誤 -
webpack-merge用來(lái)合并多個(gè)webpack的配置文件
然后創(chuàng)建配置文件common.js、dev.js、prod.js,common.js存放開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境公用的配置:
const { resolve } = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const merge = require('webpack-merge');
const prodConfig = require('./prod.js');
const devConfig = require('./dev.js');
const commonConfig = {
output: {
filename: 'js/[name].js',
chunkFilename: 'js/[name].[chunkhash].js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
}, {
loader: 'eslint-loader',
}],
}, {
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
url: false,
}
}],
}, {
test: /\.(png|jpg|jpeg|gif|bmp)(\?.+)?$/,
include: [resolve(__dirname, '../src/assets')],
exclude: [resolve(__dirname, '../public/static')],
use: [
{
loader: 'url-loader',
options: {
limit: 20480,
name: '[name].[ext]',
outputPath: 'img',
},
},
],
}, {
test: /\.(eot|ttf|woff|woff2|svg)(\?.+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts',
},
},
],
},
],
},
plugins: [
new CopyWebpackPlugin([{
from: resolve(__dirname, '../public/static'),
to: resolve(__dirname, '../dist/static'),
}]),
new webpack.HashedModuleIdsPlugin(),
new webpack.NamedModulesPlugin(),
new FriendlyErrorsWebpackPlugin(),
],
resolve: {
extensions: ['.js', '.css', '.less'],
alias: {
'@': resolve(__dirname, '../src'),
'assets': resolve(__dirname, '../src/assets'),
},
},
};
module.exports = function(env, argv) {
const config = argv.mode === 'production' ? prodConfig : devConfig;
return merge(commonConfig, config);
};
接下來(lái)配置開(kāi)發(fā)環(huán)境dev.js:
const { resolve } = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'cheap-module-eval-source-map',
output: {
path: resolve(__dirname, '../dist'),
publicPath: '',
},
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: ['style-loader', 'css-loader', 'less-loader'],
}
],
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
favicon: 'public/favicon.ico',
filename: 'index.html',
}),
new webpack.HotModuleReplacementPlugin(),
],
performance: {
hints: false,
},
stats: 'errors-only',
devServer: {
contentBase: '../dist',
hot: true,
host: 'localhost',
port: 8050,
stats: 'errors-only',
overlay: true,
}
};
修改package.json,添加script腳本:
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --progress --config webpack-config/common.js",
"build": "webpack --mode production --progress --config webpack-config/common.js"
},
}
執(zhí)行npm run dev就會(huì)看到瀏覽器自動(dòng)加載頁(yè)面,如果現(xiàn)在修改和保存任意源文件,web 服務(wù)器就會(huì)自動(dòng)重新加載編譯后的代碼,這意味著開(kāi)發(fā)環(huán)境就配置完成了。
寫(xiě)幾個(gè)頁(yè)面跑起來(lái)
開(kāi)始擼頁(yè)面代碼,具體可以查看項(xiàng)目代碼webpack4-skeleton,略過(guò)......
配置webpack的生產(chǎn)環(huán)境
首先安裝必要的package
npm i -D mini-css-extract-plugin uglifyjs-webpack-plugin optimize-css-assets-webpack-plugin clean-webpack-plugin
-
mini-css-extract-plugin從 bundle 中提取css到單獨(dú)的文件 -
optimize-css-assets-webpack-plugin優(yōu)化css打包 -
uglifyjs-webpack-plugin控制項(xiàng)目中 UglifyJS -
clean-webpack-plugin清除上次打包的文件
編輯prod.js:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'none',
output: {
path: resolve(__dirname, '../dist/static'),
publicPath: './static/', // 使用相對(duì)路徑而不是絕對(duì)路徑可以避免在hybird App框架中因?yàn)檎也坏铰窂綀?bào)錯(cuò)
},
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
'css-loader',
'less-loader',
],
}
],
},
plugins: [
new CleanWebpackPlugin(['dist/*'], {
root: resolve(__dirname, '../'), // 指定root可以避免由于清理目錄超出根目錄而跳過(guò)清理
}),
new HtmlWebpackPlugin({
template: 'public/index.html',
favicon: 'public/favicon.ico',
filename: '../index.html',
}),
new MiniCssExtractPlugin({
filename: "css/[name].css",
chunkFilename: "css/[name].[chunkhash].css",
}),
],
optimization: {
runtimeChunk: true,
splitChunks: {
chunks: 'all',
name: true,
},
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: false, // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({})
],
},
performance: {
hints: 'warning',
assetFilter: function(assetFilename) {
return assetFilename.endsWith('.js') || assetFilename.endsWith('.css');
},
},
stats: 'errors-only',
};
執(zhí)行npm run build,打包生產(chǎn)環(huán)境的代碼。