最基本的單入口配置
以前使用webpack1.x的能夠讀讀這篇文章《webpack升級指南和特性摘要》javascript
module.exports={
entry:'./src/index.js'
output:{
path:__dirname+'/build', //打包路徑
publicPath:publicPath, //靜態資源相對路徑
filename:`[name].js` //打包後的文件名,[name]是指對應的入口文件的名字
}
}複製代碼
以上是單入口的基本配置,多頁面項目首先須要將入口文件變爲多入口,結構相似這樣css
{
a:'./src/a.js',
b:'./src/b.js'
}複製代碼
那麼問題來了,如何將入口文件變成這種形式呢?這裏須要用到 node 的fs模塊(fs模塊使用文檔),經過 fs.readdirSync
方法獲取文件名(fs使用以前須要require該模塊的),多層目錄須要遞歸獲取,具體操做方法以下:html
工具方法能夠單獨放在 webpack.uitl.js
中vue
//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;//對外吐出該方法,供其餘文件使用複製代碼
方法寫好後,多入口的基本配置就能夠這麼寫:java
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
對象中的鍵名。
node
loader配置
這裏列出經常使用的loader,根據使用的技術框架,可能會有些差異,個人項目用的是 react
,因此 babel-loader
會匹配jsx
,若是使用其餘框架,則按需配置loader,例如使用vue
,則須要新增長一個 vue-loader
(具體請自行google)react
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 配置
多數狀況下,咱們不僅是須要打包js文件,而是須要將html頁打包,可是html文件是不能做爲入口文件的,用fs模塊也能夠將html拷貝帶build,可是文件裏的引用關係就麻煩了,這個時候咱們就能夠藉助一個插件 html-webpack-plugin
來幫助咱們完成該工做,直接上代碼,解釋寫在註釋中:jquery
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 //不壓縮
})
)
}
})複製代碼
項目中有許多js是屢次被引用的,webpack
是會將這些js打包全部 import
過它們的js中,這樣會致使打包後的js文件都很是龐大,對此 webpack
內置了一個插件 optimize.CommonsChunkPlugin
,根據你的配置,會將屢次被引用的文件打包到一個公用的js文件中,操做以下:webpack
// 公共代碼單獨打包
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"'
}
})複製代碼
默認狀況下,webpack是將依賴的css以style標籤的形式插入到head中,文件依賴多了,也會使打包後的文件過大,extract-text-webpack-plugin
能夠將css文件打包成一個公共的css文件,而後以link的方式打包到html的head中:git
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複製代碼
暫時整理的就這麼多,後期有用到新的會繼續跟進,有錯誤的地方還忘指出,謝謝!! 最後貼出完整的配置:
完整配置
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;複製代碼
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"'
}
})
])
}複製代碼
{
"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"
}
}複製代碼