webpack2教程--1webpack的安裝和webpack.config.js介紹

之前都是gulp之類的,現在都什麼時代了!是時候學習webpack,現在都webpack4.x了,沒辦法,我這種弱雞就只能webpack2開始。
webpack官網文件

前提

  1. 安裝node,安裝成功,npm -vcmd命令來檢查是否安裝成功。
  2. 安裝webpack,npm install [email protected]局部安裝,全局安裝使用npm install [email protected] -g,如果忘記版本號,npm view webpack versions --json查看所有版本號。
  3. 下載淘寶鏡像源,國內npm下載很慢,而且有些包下下來會有問題,比如node-sass。所以需要切換爲淘寶鏡像源,npm install -g cnpm --registry=https://registry.npm.taobao.org
  4. cls清空命令提示符窗口內容

webpack(我的系統是win10)

處理html

  1. 在根目錄下生成package.json文件: npm init -y
  2. 安裝webapck到開發模式 :cnpm install webpack --save-dev
  3. 創建webpack.config.js文件: echo > webpack.config.js
var path = require("path")
module.exports={
   <!--  要打包的文件 -->
    entry:"./index.js",
    output:{
        <!-- 指定打包後的文件名字 -->
        filename:"bundle.js",
        <!-- 打包後的文件路徑 -->
        path:path.resolve(__dirname,"dist")
    }
}
  1. 創建src目錄,並在src目錄下創建index.html,index.js文件並隨便輸入一點東西
window.onload = function(){
alert(1)
}
  1. 執行 webpack 命令,可以發現webpack幫我們在dist下生成了一個main.js文件,打開main.js並拖到最下面你會發現index.js的內容就在裏面。
    打包完之後,我們在dist生成了js文件,但是我們的index.html在src下面,你可以手動的複製src下的html文件到dist目錄下,並且將打包後的js文件引入。不過像我們這麼懶的人,還是希望webpack能夠幫我們在dist下也生成index.html,要是能自動引入打包後的js文件,那就再好不過了。這時候,是時候來一發插件了。
  2. cnpm install html-webpack-plugin --save-dev
    修改webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require("path")
module.exports = {
    entry:"./index.js",
    output: {
        path: path.resolve(__dirname, 'dist'),
         filename:"bundle.js",
    },
    plugins: [new HtmlWebpackPlugin({
        title: "測試"       
    })]
}

重新執行命令 webpack ,你會發現在dist下多生成了一個index.html文件,打開發現還有一個script的標籤引用着我們打包後的文件,nice。

不過問題又來了,html文件很簡陋,就是emmet幫我們生成的Html5文件,你可能希望還帶有更多的 meta標籤,像這樣的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta name="format-detection" content="telephone=no, email=no"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
</head>
<body>
    <header></header>
    <nav></nav>
    <div id="app"></div>
    <footer></footer>
</body>

— 此時,你可以自己寫一個模板,只需要告訴html-webpack-plugin插件文件的位置就可以了。
修改webpack.config.js

plugins: [
    new htmlWebpackPlugin({
        title:"首頁",
        <!-- 指定模板位置 -->
        template:'./src/index.html',
        <!-- 指定打包後的文件名字 -->
        filename: 'index.html'
    })
]

對於多疑症的你,打包多次後你可能會懷疑文件修改生效了沒有,此時你可以安裝clean-webpack-plugin插件,在每次打包時,先刪除原來的dist目錄,再重新打包,這樣就可以放心的睡覺,不用擔心門沒關了。
11. cnpm install clean-webpack-plugin --save-dev

plugins:[
    new htmlWebpackPlugin({
        title:"首頁",
        template:'./src/index.html',
        filename: 'index.html'
    }),
    <!-- 每次打包前先刪除dist目錄 -->
    new CleanWebpackPlugin(['dist'])
]

處理css

以往我們寫css都是寫好後手動通過link引入到html,使用webpack後,你將不再需要手動做這些操作,只需要在js文件中引入,webpack就能幫你搞定,不過需要一些loader和plugin的支持。
cnpm install --save-dev css-loade style-loader
修改webpack.config.js

###爲了處理css文件我們需要多配置一個module參數,並使用css-loader來將css文件打包到成“字符串”到js文件中,並使用style-loader將打包後的字符串提取出來並append<style>標籤到head下面 

var path=require("path");
var htmlWebpackPlugin=require('html-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports={
  entry:{
    main:'./src/index.js'
  },
  output:{
    filename:"bundle.js",
    path:path.resolve(__dirname,'dist')
  },
  module:{
    rules:[   
        <!-- test檢測到以xxx結尾的東西use對應的loader -->
        {
            test: /\.css$/,
            use: [ 'style-loader', 'css-loader' ]         
        }
    ]
  },
  plugins:[
    new htmlWebpackPlugin({
      title:"首頁",
      template:'./src/index.html',
      filename:"index.html"
    }),
    new CleanWebpackPlugin(['dist'])    
  ]
}

哦,聽說你想用sass 預處理 器,那麼只需要在use里加多一個sass-loader,並安裝依賴
cnpm install --save-dev sass-loader node-sass

rules:[  
    {
        test: /\.scss$/,
        use: [ 'style-loader', 'css-loader',"sass-loader" ]         
    }
]

什麼,想要自動補全瀏覽器後綴autoprefixer?沒問題

rules:[  
        {
            test: /\.s?css$/,
            use: [
                    {
                        loader: "style-loader"
                    }, 
                    {
                        loader: "css-loader"
                    }, 
                    {
                        loader: 'postcss-loader',
                        options: {
                            plugins:  [     
                                 require('autoprefixer')({
                                  browsers: ['last 10 versions']
                              })
                            ]
                        }
                    },
                    {
                        loader: "sass-loader"
                    }
                ]         
        }
    ]

這裏需要注意,use裏面的執行順序是倒序的 ,,webpack會以倒敘的方式處理並將處理後的結果返回給上一個loader,最後通過style-loader將文件的內容append到head下面的style標籤裏。

我還想要自動刷新呢?

** 當你修改一點文件後,你需要重執行命令新編譯文件,然後刷新頁面。如果你使用過gulp的自動刷新比如live-reload,那你也一定希望webpack也有同樣的功能,webpack-dev-server滿足你的需求並且能夠給你更多**

cnpm install --save-dev webpack-dev-server

<!--package.json中加入: -->
"scripts": {
    "dev": "webpack-dev-server"
}

通過npm run dev即可執行創建一個服務器,打開localhsot:8080
此時再修改文件,你會發現頁面自動刷新了並且修改生效了,不過你看不到重新編譯後的文件在哪裏,應爲webpack-dev-server將文件編譯到了內存中 ,比起重新生成文件效率會更高,當然只適用於開發階段。

啓動服務後,如果你 還想讓他自己 打開Localhost,還想 使用模塊熱重載 ,可以加多一個配置

devServer:{
         open:true,  
         hot: true,// 告訴 dev-server我們想用HMR模式                   
  }

開發的時候我們並不在意 style這種形式,但是我們希望在生產環境下 css能從js文件宏分離出來,我們希望能css能跟js並行加載,而且可以避免因爲Js文件過大,加載慢而產生的flash of unstyle(無樣式頁面閃爍)。
**使用“extract-text-webpack-plugin”插件來分離css代碼。 **

cnpm install --save-dev extract-text-webpack-plugin
修改webpack.config.js

var path=require("path");
var webpack=require('webpack');
var htmlWebpackPlugin=require('html-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");

var extractSass = new ExtractTextPlugin({
    filename: "[name].[contenthash].css",
    disable: process.env.NODE_ENV === "development"
});

//通過設置環境變量來告訴webpack我們在development模式下不需要提取css到單獨在文件,只有當不是development下(也即是production)才需要提取文件。

module.exports={
  entry:{
    main:'./src/index.js'
  },
  output:{
    filename:"bundle.js",
    path:path.resolve(__dirname,'dist')
  },
  devServer:{
         open:true,  
         hot: true, <!-- 告訴 dev-server我們想用HMR模式 -->                
  },
  module:{
    rules:[
        {
        test: /\.s?css$/,
              use: extractSass.extract({

                  use: [
                  {
                      loader: "css-loader"
                  }, 
                  {
                    loader: 'postcss-loader',
                    options: {
                      plugins:  [     
                        require('autoprefixer')
                      ]
                    }
                  },
                  {
                      loader: "sass-loader"
                  }],
                  fallback: "style-loader"
              })
          }
    ]
  },
  plugins:[
    <!-- 使用此插件來支持熱重載 -->
    new webpack.HotModuleReplacementPlugin(),

    new htmlWebpackPlugin({
      title:"首頁",
      template:'./src/index.html',
      filename:"index.html"
    }),
    new CleanWebpackPlugin(['dist']),
    extractSass
  
  ]
}
<!-- package.json -->
"scripts": {
    "dev": "set NODE_ENV=development&&webpack-dev-server",
    "build":"webpack -p"
}

分別執行npm run dev以及npm run build,你會發先npm run build時css文件被提取到一個單獨的文件了。

處理js

爲了使用promise,async等es6,7語法,同時兼容低版本瀏覽器,你還需要將js轉碼爲es5。
**
cnpm install --save-dev

"babel-core": "^6.25.0",
"babel-loader": "^7.0.0",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.5.2",

**
webpack.config.js修改

{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: ['env'],
            plugins: ['transform-runtime']
        }
    }
}

寫一點async或者prmise代碼,打包後發現promise變成了_promise2

代理

前後端分離開發時經常還會遇到跨域的問題,還可以利用devServer來進行代理

devServer:{
    open:true,  
    hot: true, 
    proxy: {
        '/api/': {
            target: 'http://baidu.com',
            secure: false,
            changeOrigin: true
        }
    }

$.ajax({
    url:'/api/',
    success:function(res){
        console.log(res)
    },
    error:function(res){
        console.log(res)
    }
})

圖片,字體

{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url-loader',
    query: {
        limit: 8000,
        <!-- 小於8k的轉化爲Base64 -->
        name: '[name].[hash:7].[ext]'
    }
}, 

{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    loader: 'url-loader',
    query: {
        limit: 8000,
         name: 'font/[name].[hash:7].[ext]'
    }
}    

你可能想要把所有img或者font文件分別放到一個img或者font文件夾下,可以這麼寫:name: 'img/[name].[hash:7].[ext]'
如果你不想下載字體文件下來,可已上傳到阿里字體庫並使用阿里的在線字體圖標,並複製自己的文件的在線地址。
在css裏面像這麼用:@import url("//at.alicdn.com/t/font_s0zqwb6by3lhm2t9.css");

打包多文件

var path=require("path");
var webpack=require('webpack');
var htmlWebpackPlugin=require('html-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");

var extractSass = new ExtractTextPlugin({
    filename: "[name].[contenthash].css",
    disable: process.env.NODE_ENV === "development"
});

module.exports={
  entry:{
    "main":'./src/js/index.js',
    "car":"./src/js/car.js",
    "goods":"./src/js/goods.js"
  }, 
 
  output:{
    filename:"[name].[hash].js",
    path:path.resolve(__dirname,'dist')
  },
 
  devServer:{
        open:true,  
        hot: true,
        proxy: {
            '/api/': {
                target: 'http://baidu.com',
                secure: false,
                changeOrigin: true
            }
        }
        
  },

  module:{
    rules:[
    
      {
        test: /\.s?css$/,
              use: extractSass.extract({
                  use: [
                  {
                      loader: "css-loader"
                  }, 
                  {
                    loader: 'postcss-loader',
                    options: {
                        plugins:  [     
                            require('autoprefixer')({
                              browsers: ['last 10 versions']
                          })
                        ]
                    }
                  },
                  {
                      loader: "sass-loader"
                  }],
                  fallback: "style-loader"
              })
          },
          {
              test: /\.js$/,
              exclude: /node_modules/,
              use: {
                loader: 'babel-loader',
                options: {
                  presets: ['env'],
                  plugins: ['transform-runtime']
                }
              }
        },
        {
      test: /\.html$/,
      use: ['html-withimg-loader']
    },
        {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            loader: 'url-loader',
            query: {
                limit: 8000,
                name: 'img/[name].[hash:7].[ext]'
            }
        }, 
        {
            test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
            loader: 'url-loader',
            query: {
                limit: 8000,
                 name: 'font/[name].[hash:7].[ext]'
            }
        }
    ]
  },
    plugins:[
        new webpack.HotModuleReplacementPlugin(),
        new htmlWebpackPlugin({
          title:"首頁",
          template:'./src/index.html',
          filename:"index.html",
          chunks:["page/main/main"]
        }),
        new htmlWebpackPlugin({
          title:"購物車",
          template:'./src/index.html',
          filename:"car.html",
          chunks:["page/car/car"]
        }),
        new htmlWebpackPlugin({
          title:"商品",
          template:'./src/index.html',
          filename:"goods.html",
          chunks:["page/goods/goods"]
        }),

        new CleanWebpackPlugin(['dist']),
        extractSass,
        new webpack.ProvidePlugin({
             $:"jquery",
             jQuery:"jquery"
        }) 
        
    ]
}

上面這樣寫打包後文件都放在一個目錄下,目錄很亂,不方便管理,想讓每個頁面的js,css文件放在對應目錄下,可以按下面這麼寫

entry:{
    "page/main/main":'./src/js/index.js',
    "page/car/car":"./src/js/car.js",
    "page/goods/goods":"./src/js/goods.js"
  }

如果多個文件都引用了一些其他庫,比如Jquery,vue,你可能想把所有的公共庫提取出來,利用common-chunk插件可以解決。即使你做的是spa單頁面應用,你也可以將公共庫從js文件中提取出來,每次修改時只修改業務邏輯而不重新打包庫,從而可以緩存庫文件。

entry:{
        "main/main":'./src/js/index.js',
        "car/car":"./src/js/car.js",
        "goods/goods":"./src/js/goods.js"
        "vendor":["jquery","vue"]
  },

plugins:[     
        new htmlWebpackPlugin({
          ...同上
          chunks:["page/main/main","vendor","mainfest"]
        }),

        new htmlWebpackPlugin({
          ...
          chunks:["page/car/car","vendor","mainfest"]
        }),

        new htmlWebpackPlugin({
          ...
          chunks:["page/goods/goods","vendor","mainfest"]
        }),

         new webpack.optimize.CommonsChunkPlugin({
            name:["vendor","mainfest"]
        })
    ]

可以發現,我們在entry中手動引入了多個文件,在plugins中,我們又手動的多次new htmlWebpackPlugin()。雖然能夠正常的運行,然而存在很多限制。首先存在大量重複的代碼,現在只有3個頁面,假如現有10個頁面,那麼你就得手動的new10次,在enrty中輸入10個入口。其次,當增加新的頁面時,又得過來webpack的配置裏面手動的再加入到入口文件中。明顯的,對於這些重複的而且長得很像的,如果能自動獲取到需要配置的文件,並且在一個循環裏面調用,那就方便多了。

使用node.js的golb模塊來獲取文件,並且使用webpack-merge來合併對象

//引入glob
var glob= require('glob');
//同步讀取src目錄下所有的html文件
var files = glob.sync('./src/*.html');
var entry={};
var plugins=[];
//循環將文件
files.forEach(function(item,i){
//item類似:./src/index.html
var htmlName=item.slice(item.lastIndexOf("/")+1);
//最後生成的文件名只需要最後面的名字index.html
var name=htmlName.split(".")[0];
//添加到entry入口,並制定生成文件的目錄
entry["page/"+name+"/"+name]='./src/js/'+name+'.js'

//生成htmlWebpackPlugin實例
plugins.push(
    new htmlWebpackPlugin({
        template:item,
        filename:htmlName,
        chunks:["page/"+name+"/"+name/*]
    })
)
});
module.exports={
entry:entry,
output:{
filename:"[name].[chunkhash].js",
path:path.resolve(__dirname,'dist'),
},
module:{
rules:[
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},

]
},
plugins:plugins
}

可以發現,使用glob.sync來讀取文件並進行循環操作,原本需要手動操作的部分,循環已經幫我們處理好了。原本需要很多new new htmlWebpackPlugin()已經不再存在,而且即使後續需要添加新的頁面,也不需要我們手動去添加,glob.sync會自己去讀取所有的*.html文件,接着循環會幫我們處理。這裏唯一需要注意的地方就是,index.html對應的js文件必須爲index.js,ceshi.html文件對應的文件必須爲ceshi.js,至於js裏面需要引入其他庫,或者自己寫的工具函數,哪個頁面只需引入即可,webpack會自己處理好。

提取相同的庫文件到單獨的文件中
當頁面存在引用相同的庫時,最好還是將這些庫文件提取到單獨的文件,頁面只保留業務邏輯的js代碼。爲了提取文件,有2種處理方案:

  1. 使用CommonsChunkPlugin來提取在上面的代碼上增加:(以下這部分代碼只在使用CommonsChunkPlugin時需要)
entry.vendor=[
//舉例如下:
"vue","vuex","axios","jquery","vue-router","moment","lodash"
];
plugins=[

new webpack.optimize.CommonsChunkPlugin({
name:["vendor","manifest"]
})
]
//修改循環中的chunks, 增加vendor以及manifest:
files.forEach(function(item,i){
plugins.push(
new htmlWebpackPlugin({
template:item,
filename:htmlName,
chunks:["page/"+name+"/"+name,"vendor","mainfest"]
})
)
});

再執行webpack,可以發現公共的庫已經被提取到單獨的vendor文件。
2. 使用DLL
對於 CommonsChunkPlugin,webpack 每次打包實際還是需要去處理這些第三方庫,只是打包完之後,能把第三方庫和我們自己的代碼分開。而DLLPlugin 則是能把第三方代碼完全分離開,即每次只打包項目自身的代碼。

這裏我使用的是autodll-webpack-plugin。
npm install --save-dev autodll-webpack-plugin

//引入模塊
var AutoDllPlugin = require('autodll-webpack-plugin');
//在plugins中添加插件:(需要放在循環之前,防止數組被重新賦值)
var plugins=[
new AutoDllPlugin({
//文件名
filename: '[name].[hash].js',
    //打包後的文件路徑
    path: './page/common/',

    //是否將生成的Js文件注入到html文件中
    inject:true,

    //需要分離的庫
    entry: {
      vendor: [
        "vue","vuex","axios","jquery","vue-router","moment","lodash","vue-touch","vue-lazyload"
      ]
    },

    //壓縮代碼(注意這裏是壓縮分離出來的庫文件代碼,跟外面的要區分開來,如果外面的需要壓縮,外面的plugins中也需要new一個壓縮實例)
    plugins: [
      new webpack.optimize.UglifyJsPlugin()
    ]
  })
  ]

執行webpack命令,可以發現公共的庫文件也被打包到單獨的文件了。autodll-webpack-plugin在內存中緩存了文件,所以當你第二此執行webpack命令進行打包時,可以明顯發現打包時間少了很多。而CommonsChunkPlugin每次都全部重新打包再進行分離,所以當需要多次修改文件時,autodll-webpack-plugin可以明顯降低打包的時間,尤其是依賴很多外部庫的時候。

使用webpack-merge區分生成環境和開發環境:
很多時候,我們都需要針對不同的環境進行不用的操作。

//比如在生成環境下分離css到單獨文件:
var extractSass = new ExtractTextPlugin({
filename: "[name].[contenthash].css",
disable: process.env.NODE_ENV === "development"
});
//比如在生成環境下要壓縮代碼:
new UglifyJSPlugin()//在開發環境下使用代理
devServer:{
proxy: {
'api': {
target: 'http://api.douban.com/v2/movie/',
secure: false,
changeOrigin: true
}
}

通常來說會用process.env.NODE_ENV === "development",並且在package.json中設置環境變量來進行判斷,不過當文件大了或者頁面需要判斷的地方多了之後,配置文件就會充斥着大量三元表達式。可以考慮用webpack-merge將配置文件拆分爲3個文件,一個是webpack.common.js,即不管是生產環境還是開發環境都會用到的部分,以及webpack.product.jswebpack.dev.js

//webpack.common.js
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules:[
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
]

},
plugins: [
new HtmlWebpackPlugin({
title: 'test'
})
],
}
//開發環境webpack.dev.js
var merge = require('webpack-merge');
var common = require('./webpack.common.js');
module.exports = merge(common, {
module:{
rules:[
{
test: /.css$/,
use:["style-loader","css-loader"]
}
]

},
devtool: 'inline-source-map',
devServer:{
open:true,

hot: true,
proxy: {
'/api/': {
target: 'http://baidu.com',
secure: false,
changeOrigin: true
}
}

},
})
//生產環境webpack.product.js
var merge = require('webpack-merge');
var UglifyJSPlugin = require('uglifyjs-webpack-plugin');
var common = require('./webpack.common.js');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var cleanPlugin = require("clean-webpack-plugin");
var extractSass = new ExtractTextPlugin({
filename: "[name].[contenthash].css",
});
module.exports = merge(common, {
module: {
rules: [{
test: /.css$/,
use: extractSass.extract({
use: [{
loader: "css-loader"
}, ],
fallback: "style-loader"
})
}]
},
devtool: 'source-map',
plugins: [
    new cleanPlugin(["dist"]),
    new UglifyJSPlugin(),
    extractSass,
]

});

在package.json中修改webpack默認配置文件

"scripts": {
"dev": "webpack-dev-server --config webpack.dev.js",
"build": "webpack --config webpack.product.js"
},

通過npm run dev來開發,及npm run build來打包。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章