最基本的單入口配置
之前使用webpack1.x的可以讀讀這篇文章《webpack升級指南和特性摘要》
module.exports={
entry:'./src/index.js'
output:{
path:__dirname+'/build', //打包路徑
publicPath:publicPath, //靜態資源相對路徑
filename:`[name].js` //打包後的檔名,[name]是指對應的入口檔案的名字
}
}複製程式碼
以上是單入口的基本配置,多頁面專案首先需要將入口檔案變為多入口,結構類似這樣
{
a:'./src/a.js',
b:'./src/b.js'
}複製程式碼
那麼問題來了,如何將入口檔案變成這種形式呢?這裡需要用到 node 的fs模組(fs模組使用文件),通過 fs.readdirSync
方法獲取檔名(fs使用之前需要require該模組的),多層目錄需要遞迴獲取,具體操作方法如下:
工具方法可以單獨放在 webpack.uitl.js
中
webpack.util.js
//getAllFileArr方法
//遞迴獲取檔案列表可能會在多處用到,所以可以封裝成方法
//最後返回的資料格式如下
/*
[ [ 'a.js', './src/scripts/', './src/scripts/a.js' ],
[ 'b.js', './src/scripts/', './src/scripts/b.js' ],
[ 'c.js', './src/scripts/', './src/scripts/c.js' ] ]
*/
function getAllFileArr(path){
let AllFileList=[];
getAllFile(path)
function getAllFile(path) {
var files = [];
if( fs.existsSync(path) ) { //是否存在此路徑
files = fs.readdirSync(path); //獲取當前目錄下一層檔案列表
files.forEach((file,index)=>{ //遍歷獲取檔案
var curPath = path + "/" + file;
if(fs.statSync(curPath).isDirectory()) { // recurse 檢視檔案是否是資料夾
getAllFile(curPath); //如果是資料夾,繼續遍歷獲取
} else {
if(file!=='.DS_Store'){
//.DS_Store是IDE配置檔案不用計入到檔案列表
AllFileList.push([file,path,curPath])
}
}
});
}
};
return AllFileList;
}
exports.getAllFileArr=getAllFileArr; //對外吐出該方法,供其他檔案使用複製程式碼
//getEntry方法
//最後返回如下資料結構
/*
{
a: './src/scripts/a.js',
b: './src/scripts/b.js',
c: './src/scripts/c.js'
}
*/
function getEntyry(path){
let file_list=getAllFileArr(path);
let entry={};
file_list.forEach((item)=>{
entry[item[0].split('.').slice(0,-1).join('.')]=item[2] //鍵名去掉檔案字尾
})
return entry;
}
exports.getEntry = getEntry;//對外吐出該方法,供其他檔案使用複製程式碼
方法寫好後,多入口的基本配置就可以這麼寫:
webpac.config.js
const utils = require('./webpack.util.js')
module.exports={
entry:utils.getEntyry('./src/script') //使用getentry方法獲取多入口
output:{
path:__dirname+'/build', //打包路徑
publicPath:publicPath, //靜態資源相對路徑
filename:`[name].js` //打包後的檔名,[name]是指對應的入口檔案的名字
}
}複製程式碼
如上配置執行 webpack
命令後,會將入口檔案中所有的成員都打包到 build
下,檔名為 entry
物件中的鍵名。
loader配置
這裡列出常用的loader,根據使用的技術框架,可能會有些差別,我的專案用的是 react
,所以 babel-loader
會匹配jsx
,如果使用其他框架,則按需配置loader,例如使用vue
,則需要新增加一個 vue-loader
(具體請自行google)
module:{
rules:[
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader',
},{
test:/\.(css|scss)$/,
loader:"style-loader!css-loader!postcss-loader!sass-loader" //webpack2.x是不支援loader簡寫的,這裡稍微注意一下
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff)$/,
loader: 'file-loader',
options: {
name: 'source/[name].[ext]?[hash]' //該引數是打包後的檔名
}
}
]
},複製程式碼
webpack plugins 配置
html單獨打包外掛
多數情況下,我們不只是需要打包js檔案,而是需要將html頁打包,但是html檔案是不能作為入口檔案的,用fs模組也可以將html拷貝帶build,但是檔案裡的引用關係就麻煩了,這個時候我們就可以藉助一個外掛
html-webpack-plugin
來幫助我們完成該工作,直接上程式碼,解釋寫在註釋中:
const htmlWebpackPlugin = require('html-webpack-plugin')
const utils = require('./webpack.util.js')
module.exports={
entry:{
//注意,webpack.optimize.CommonsChunkPlugin打包時候的chunk是跟entry的鍵名對應的
app:'./src/app.js',
lib:['src/lib/fastClick.js','src/lib/vConsole.js']
}
output:{
path:__dirname+'/build', //打包路徑
publicPath:publicPath, //靜態資源相對路徑
filename:`[name].js` //打包後的檔名,[name]是指對應的入口檔案的名字
}
}
//遍歷所有需要打包的html檔案,分別配置打包
var html_list=utils.getAllFileArr('./src');
html_list.forEach((item)=>{
var name = item[2];
if(/\.html$/.test(item[0])){
var prex='' //檔案字首,如果想給打包的html放在build下的html資料夾中,則var prex='html/'
module.exports.plugins.push( //每個檔案分別配置外掛
new htmlWebpackPlugin({
favicon: './src/images/favicon.ico', //favicon路徑,通過webpack引入同時可以生成hash值
filename: prex+item[0],
template: name, //html模板路徑
inject: true, //js插入的位置,true/'head'/'body'/false
hash: true, //為靜態資源生成hash值
chunks: [item[0].slice(0,-5),'common'],//需要引入的chunk,不配置就會引入所有頁面的資源
minify: { //壓縮HTML檔案
removeComments: true, //移除HTML中的註釋
collapseWhitespace: false, //刪除空白符與換行符
ignoreCustomFragments:[
// regexp //不處理 正則匹配到的 內容
]
},
minify: false //不壓縮
})
)
}
})複製程式碼
webpack.optimize.CommonsChunkPlugin外掛(webpack內建外掛文件)
專案中有許多js是多次被引用的,
webpack
是會將這些js打包所有import
過它們的js中,這樣會導致打包後的js檔案都非常龐大,對此webpack
內建了一個外掛optimize.CommonsChunkPlugin
,根據你的配置,會將多次被引用的檔案打包到一個公用的js檔案中,操作如下:
// 公共程式碼單獨打包
new webpack.optimize.CommonsChunkPlugin({
name: 'common', //對外吐出的chuank名
chunks:['app','lib'], //陣列,需要打包的檔案[a,b,c]對應入口檔案中的key
minChunks:4, //chunks至少引用4次的時候打包
filename: 'script/[name].js' //打包後的檔名
})
//以及一些其他的常用webpack內建外掛
//壓縮編譯後的程式碼,加了這個外掛後編譯速度會很慢,所以一般在生產環境加
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
/*
UglifyJsPlugin 將不再支援讓 Loaders 最小化檔案的模式。debug 選項已經被移除。Loaders 不能從 webpack 的配置中讀取到他們的配置項。
loader的最小化檔案模式將會在webpack 3或者後續版本中被徹底取消掉.
為了相容部分舊式loader,你可以通過 LoaderOptionsPlugin 的配置項來提供這些功能。
*/
new webpack.LoaderOptionsPlugin({
minimize: true
}),
//程式碼合併壓縮
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
})複製程式碼
extract-text-webpack-plugin外掛 (extract-text-webpack-plugin文件)
預設情況下,webpack是將依賴的css以style標籤的形式插入到head中,檔案依賴多了,也會使打包後的檔案過大,extract-text-webpack-plugin
可以將css檔案打包成一個公共的css檔案,然後以link的方式打包到html的head中:
module:{
rules:[
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader',
},{
test:/\.(css|scss)$/,
//注意,使用ExtractTextPlugin時,css相關的loader配置需要修改成如下
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader!postcss-loader!sass-loader"
})
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff)$/,
loader: 'file-loader',
options: {
name: 'source/[name].[ext]?[hash]' //該引數是打包後的檔名
}
}
]
},
new ExtractTextPlugin("[name].css?[hash]") //基礎配置只需要傳入打包名稱就行了複製程式碼
devserver
webpack-dev-server
基本上使用webpack構建時,除錯階段必用的,具體引數在 webpack官方文件 解釋的比較詳細,這裡就不多說了,簡單的貼一下程式碼:
devServer: {
contentBase: "./dist",//本地伺服器所載入的頁面所在的目錄
//historyApiFallback: true, //非hash模式路由不重新整理(適用於單頁面開發除錯)
noInfo:true,
host:'192.168.102.103',
port:'4001'
},複製程式碼
檔案拷貝
有些時候,我們需要將某些檔案直接拷貝到build目錄下,如某些xml配置檔案,通過 fs.createReadStream
和 fs.createWriteStream
進行檔案的拷貝和移動(詳細說明請看 fs模組使用文件):
module.exports.plugins.push(function(){
//打包完畢後將devconfig.xml檔案移動到build目錄下
return this.plugin('done', function(stats) {
// 建立讀取流
var readable = fs.createReadStream( './devconfig.xml');
// 建立寫入流
var writable = fs.createWriteStream( './build/config.xml' );
// 通過管道來傳輸流
readable.pipe( writable );
});
});複製程式碼
專案釋出
在開發的階段,我們往往不需要讓檔案打包到最優狀態,因為需要保證打包速度,但是在釋出的時候需要打包到最優狀態,這就需要我們對開發和生產兩種模式做不同的處理,我是採用 cross-env
這個包獲取NODE_ENV的值來判斷當前是什麼環境:
if (process.env.NODE_ENV === 'production') {
//生產模式下進行打包優化
}複製程式碼
如何來改變NODE_ENV的值呢? cross-env
可以幫助我們通過命令來修改, 執行以下命令,就能將 process.env.NODE_ENV
的值變為'development'
$ cross-env NODE_ENV=development複製程式碼
暫時整理的就這麼多,後期有用到新的會繼續跟進,有錯誤的地方還忘指出,謝謝!! 最後貼出完整的配置:
完整配置
webpack.util.js
let fs =require('fs')
//獲取入口檔案物件
function getEntry(file_list){
var entry={};
file_list.forEach((item)=>{
entry[item[0].split('.').slice(0,-1).join('.')]=item[2]
})
return entry;
/*entry 看起來就是這樣
{
a: './src/scripts/a.js',
b: './src/scripts/b.js',
index: './src/scripts/index.js'
}
*/
}
exports.getEntry = getEntry;
//遞迴遍歷所有檔案
function getAllFileArr(path){
var AllFileList=[];
getAllFile(path)
function getAllFile(path) {
var files = [];
if( fs.existsSync(path) ) { //是否存在此路徑
files = fs.readdirSync(path);
files.forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.statSync(curPath).isDirectory()) { // recurse 檢視檔案是否是資料夾
getAllFile(curPath);
} else {
if(file!=='.DS_Store'){
AllFileList.push([file,path,curPath])
}
}
});
}
};
/*
最後AllFileList 看起來就是這樣
[ [ 'a.js', './src/scripts/', './src/scripts/a.js' ],
[ 'b.js', './src/scripts/', './src/scripts/b.js' ],
[ 'index.js', './src/scripts/', './src/scripts/index.js' ] ]
*/
return AllFileList;
}
exports.getAllFileArr=getAllFileArr;
//刪除資料夾 ,遞迴刪除
function deleteFolderRecursive(path) {
var files = [];
if( fs.existsSync(path) ) {
files = fs.readdirSync(path);
files.forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.statSync(curPath).isDirectory()) { // recurse 檢視檔案是否是資料夾
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
};
exports.deleteFolderRecursive=deleteFolderRecursive;複製程式碼
webpack.config.js
const path =require('path');
const fs =require('fs')
const webpack = require('webpack');
const htmlWebpackPlugin = require('html-webpack-plugin')
let ExtractTextPlugin = require('extract-text-webpack-plugin')
const utils = require('./webpack.util.js')
//打包之前刪除build資料夾
utils.deleteFolderRecursive('./build')
let publicPath='./'
,updateTime=new Date().getTime()
module.exports={
entry:{
...utils.getEntry(utils.getAllFileArr('./src/script')),
react:'react',
jquery:'jquery'
},
output:{
path:__dirname+'/build',
publicPath:publicPath,
filename:`script/[name].js`
},
module:{
rules:[
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader',
},{
test:/\.(css|scss)$/,
// loader:"style-loader!css-loader!postcss-loader!sass-loader"
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader!postcss-loader!sass-loader"
})
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff)$/,
loader: 'file-loader',
options: {
name: 'source/[name].[ext]?[hash]'
}
}
]
},
resolve:{
extensions:['.scss', '.js','.jsx'],
alias: {
'bassCss':__dirname+'/src/css',
'image':__dirname+'/src/image',
'components':__dirname+'/src/script/components'
}
},
devServer: {
// contentBase: "./dist",//本地伺服器所載入的頁面所在的目錄
// historyApiFallback: true, //不跳轉
noInfo:true,
host:'192.168.102.103',
port:'4001'
},
plugins:[
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
}),
// 公共程式碼單獨打包
new webpack.optimize.CommonsChunkPlugin({
name: 'common', //對外吐出的chuank名
chunks:Object.keys(utils.getEntry(utils.getAllFileArr('./src/script'))), //陣列,需要打包的檔案[a,b,c]對應入口檔案中的key
minChunks:4, //chunks至少引用4次的時候打包
filename: 'script/[name].js' //打包後的檔名
})
]
}
module.exports.plugins.push(new ExtractTextPlugin("[name].css?[hash]"))
//複製 config.xml 到 build目錄下
module.exports.plugins.push(function(){
return this.plugin('done', function(stats) {
// 建立讀取流
var readable = fs.createReadStream( './devconfig.xml');
// 建立寫入流
var writable = fs.createWriteStream( './build/config.xml' );
// 通過管道來傳輸流
readable.pipe( writable );
});
});
//將html檔案打包
var html_list=utils.getAllFileArr('./src');
html_list.forEach((item)=>{
var name = item[2];
if(/\.html$/.test(item[0])){
var prex=''//item[1].indexOf('html')>-1?'html/':''
module.exports.plugins.push(
new htmlWebpackPlugin({ //根據模板插入css/js等生成最終HTML
// favicon: './src/images/favicon.ico', //favicon路徑,通過webpack引入同時可以生成hash值
filename: prex+item[0],
template: name, //html模板路徑
inject: true, //js插入的位置,true/'head'/'body'/false
hash: true, //為靜態資源生成hash值
chunks: [item[0].slice(0,-5),'common'],//需要引入的chunk,不配置就會引入所有頁面的資源
minify: { //壓縮HTML檔案
removeComments: true, //移除HTML中的註釋
collapseWhitespace: false, //刪除空白符與換行符
// ignoreCustomFragments:[
// /\{\{[\s\S]*?\}\}/g //不處理 {{}} 裡面的 內容
// ]
},
minify: false //不壓縮
})
)
}
})
//生產模式打包的時候進行程式碼壓縮合並優化
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#eval-source-map'
module.exports.output.publicPath='./'
//釋出時給檔名加上時間
module.exports.plugins[module.exports.plugins.length-1]=new ExtractTextPlugin(`css/${updateTime}_[name].css?[hash]`);
module.exports.output.filename=`script/${updateTime}_[name].js`;
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
})
])
}複製程式碼
package.json
{
"name": "yit01",
"version": "1.0.0",
"description": "",
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "cross-env NODE_ENV=development webpack --progress --hide-modules --watch",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"cross-env": "^5.1.1",
"css-loader": "^0.28.7",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.5",
"html-webpack-plugin": "^2.30.1",
"node-sass": "^4.6.0",
"postcss-loader": "^2.0.8",
"sass-loader": "^6.0.6",
"scss-loader": "0.0.1",
"style-loader": "^0.19.0",
"webpack": "^3.8.1",
"webpack-dev-server": "^2.9.4"
},
"dependencies": {
"jquery": "^3.2.1",
"js-md5": "^0.7.2",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-fastclick": "^3.0.2",
"react-lazyload": "^2.3.0",
"react-redux": "^5.0.6",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"redux": "^3.7.2"
}
}複製程式碼