webpack前端工程中扮演的角色愈來愈重要,它也是前端工程化很重要的一環。本文將和你們一塊兒按照項目流程學習使用wbepack,媽媽不再用擔憂我不會使用webpack,哪裏不會看哪裏。這是一個由淺入深的文章。javascript
這裏是一個項目工程化,規範化的設置,若是是初次使用webpack的同窗仍是最後在看這一塊知識css
如今vue、react等腳手架都會自動將開發環境使用的webpack的配置文件和生產環境的配置文件分開,將壓縮代碼,添加hash控制版本等操做放在項目上線時運行,這樣避免了在開發階段打包時間過長的問題。好比像這樣,把兩個環境的配置文件分開。html
下面來看下兩個配置文件的內容(我是用的typescript開發react,內容請忽略)
開發環境:前端
生產環境:vue
能夠看到,開發環境增長了幾個插件,這樣作的好處就是更加工程化,規範化,下降開發環境的打包時間,代碼維護性也更高。java
分開寫配置文件就要涉及到使用命令執行不一樣的配置文件,咱們可使用npm的腳本命令,咱們能夠在package.json中找到scripts,添加以下命令"build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"node
給你們解釋下這個命令的意思react
若是你不追求規範化和工程化,咱們就寫一個配置文件就好,這裏沒有硬性要求。下面咱們來說webpack的具體配置webpack
在咱們對於webpack不是特別熟練的時候,咱們可能不會寫全配置文件,每每是用到什麼再去添加,下面咱們就按照這個步驟完全學會使用webpack。web
module.exports = {
entry: './src/index.js' // 這裏是項目入口文件地址 ouput: { path: __dirname + "/dist", // 這裏是項目輸出的路徑,__dirname表示當前文件的位置 filename: "js/"+"[name].js" // 這裏是生成文件的名稱,可起你想要的名字 } }
這就是咱們最初一個骨架,下面咱們再添加一些配置,好比你使用的是react,那麼你就須要添加react的相關loader,這裏以typescript編寫的react爲例。
module.exports = {
entry: './src/index.js' // 這裏是項目入口文件地址 ouput: { path: __dirname + "/dist", // 這裏是項目輸出的路徑,__dirname表示當前文件的位置 filename: "js/"+"[name].js" // 這裏是生成文件的名稱,可起你想要的名字 }, module: { rules: [ { test: /\.tsx?$/, loader: "awesome-typescript-loader" }, { enforce: "pre", test: /\.js$/, loader: "source-map-loader" } ] }, }
webpack是將一個個文件分拆成一個個模塊(module)來進行編譯打包的,咱們全部的處理文件內容的東西都要放在module裏,rules即規則。
rules裏面的兩個loader都是編譯.tsx文件及處理錯誤信息的。
在你寫好了組件以後,你須要開始編寫樣式,但不管是css仍是使用less、sass等預處理語言,webpack都是沒法直接處理的,咱們安裝並使用相應的loader。下面以less和css爲例。
{test: /\.(less|css)?$/, loader: ["style-loader", "css-loader", "less-loader"]}
webpack會按照從右到左的順序執行loader,咱們新解析less,以後進行css的打包編譯。若是你不適用less等預處理語言,安裝css-loader和style-loader便可。
寫到這裏咱們會忽然想到一個點,就是css樣式的兼容性問題,靠人工去寫的話,你內心可能會有一句mmp不值當講不當講,哈哈,咱們必須使用postcss來解決這個問題。
postcss是目前css兼容性的解決方案,會自動幫咱們加入前綴,以使css樣式在不一樣的瀏覽器能兼容,這裏安裝使用postcss-loader
{ test: /\.(less|css)?$/, loader: ["style-loader", "css-loader", "less-loader", "postcss-loader"]}
postcss-loader要寫在最後(其實只要放在css-loader以後就能夠),寫到這你覺得就能夠了嗎?只能說 too young,postcss解決兼容性問題主要靠的實際上是它的插件autoprefixer,咱們還須要在根目錄寫一個postcss.config.js的配置文件,以下
module.exports = {
plugins: [
require('autoprefixer') ] };
寫到這,咱們就不用再擔憂css兼容性問題了,就想使用babel文件同樣,這個文件會自動解析,咱們不須要管它。
咱們在開發時,每每會遇到一些圖標圖片在不一樣狀況下會失真,以及資源過多,咱們須要減少圖標類圖片的大小,這時咱們就須要引入svg,國內可能都會去使用阿里的iconfont庫,從而引入svg圖標,解決上面的問題
咱們打開下載的素材文件夾,發現裏面有一些.woff、.svg、.eot的文件,咱們要想使用svg的圖標還必須依賴這些文件,這時webpack不支持這些文件,咱們須要引入新的loader
{ test: /\.(woff|svg|eot|ttf)?$/, loader: "url-loader" }
下面咱們就能愉快的使用svg圖標了,不存在失真的狀況,同時會很小
寫到這,咱們可能不斷的打包webpack了,太麻煩了,因而乎webpack-dev-server應運而生。它是webpack提供的服務器,咱們使用npm i webpack-dev-server --save-dev來安裝。
咱們其實在命令行中敲擊webpack-dev-server --open就能夠開啓,默認是localhost:8080開啓,如今咱們不須要在重複使用webpack命令打包,安裝。
值得注意的是,webpack-dev-server打包的文件會存在內存中,因此在index.html中引入文件的時候就要以下,這裏是默認輸出文件是bundle.js
<script src="bundle.js"></script>
今天咱們不重點講webpack-dev-server,之後我會再寫文章,深刻的講解其使用。
可能咱們在開發階段只用到了這幾個功能,下面咱們來說一下項目上線的準備。
咱們打包完成的項目每每比較大,包含不少空格,佔用了很大空間,這時咱們要經過壓縮js來減少文件體積。webpack自帶了UglifyJsPlugin插件來壓縮js代碼,使用以下
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false } }) ]
咱們的插件統一放在export.modules = {}的plugins裏面,它是一個數組,使用插件時new 一個實例便可。這裏咱們使用到webpack實例,因此要在配置文件頭部引入webpack,即var webpack = require('webpack');
咱們在使用的js庫如vue或者react等的時候,webpack會將它們一塊兒打包,react和react-dom文件就好幾百k,所有打包成一個文件,可想而知,這個文件會很大,用戶在首次打開時就每每會出現白屏等待時間過長的問題,這時,咱們就須要將這類文件抽離出來。
externals: {
"react": "React", "react-dom": "ReactDOM" },
這裏咱們會用到externals,它和plugins是平級。左側key表示依賴,右側value表示文件中使用的對象。好比在react開發中,咱們經常會這樣在文件頭部寫import React from 'react',這裏你們能夠和上面對號入座下。
這裏咱們就須要對這個文件進行單獨的引入使用了,在index.html中添加以下代碼
<script src="./node_modules/react/umd/react.xxx.js"></script> <script src="./node_modules/react-dom/umd/react-dom.xxx.js"></script>
寫到這,咱們就已經將文件拆分了。
不過,咱們在項目上線的時候不可能會帶有node_modules,因此咱們就須要使用一個copy插件將react和react-dom文件複製出來
new CopyWebpackPlugin([ // from是要copy的文件,to是生成出來的文件
{ from: "node_modules/react/umd/react.xxx.js", to: "js/react.min.js" }, { from: "node_modules/react-dom/umd/react-dom.xxx.js", to: "js/react-dom.min.js" } { from: "public/favicon.ico", to: "favicon.ico" } ])
這樣咱們的index.html文件中就要寫成下面這種形式
咱們也能夠將css文件單獨拆分出來,這樣的好處就是打包的css文件咱們能夠放到cdn上,而後緩存到瀏覽器客戶端中。這樣就儘量的減少文件的體積,以及沒必要要的資源從新加載,浪費帶寬。
咱們要先安裝插件
npm install extract-text-webpack-plugin --save-dev
配置文件添加對應配置
var ExtractTextPlugin = require("extract-text-webpack-plugin");
plugins裏面添加插件
new ExtractTextPlugin("styles.css")
下面是具體的使用
module.exports = {
// entry和output自動省略
module: {
loaders: [{
test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader!postcss-loader') // 這裏我目前使用less尚未成功 }] }, postcss: function() { return [autoprefixer, cssnext, precss, cssnano] }, plugins: [ new ExtractTextPlugin('./css/[name].min.css') // 生成到css文件夾下 ] }
webpack會將全部引用到的css文件打包,最終生成./css/[name].min.css文件。
這裏對圖片進行base64進行轉碼一樣是減少資源的體積
安裝 url-loader
npm install url-loader --save-dev
在modules的rules裏面添加
{
test: /\.(png|jpg)$/, loader: 'url?limit=8192&name=images/[hash:8].[name].[ext]' }
limit 設置一個閾值,小於這個值得圖片就會自動啓用 base64 編碼的圖片,大於這個值的圖片會打包到name 這參數對應的路徑,圖片名稱就會包括8位md5編碼 name 對應文件原本名稱,ext 對應擴展名
咱們的後臺會給資源設置Cache-Control: max-age=秒替代,來對資源進行緩存時間的設置,這使得咱們在刷新頁面以後會去緩存中加載資源,可是存在一個問題,就是,一旦咱們更新版本以後,客戶沒有去清除緩存,同時緩存尚未過時的狀況下,就沒法加載到最新的資源。這時咱們就須要hash值來進行版本控制
咱們一般這樣作
output: {
path: __dirname + "/dist", filename: "[name][hash].js" }
給輸出文件加上[hash]來添加hash值,這樣就能夠作到用戶加載html裏會去加載對應hash值得打包文件,好比下面這樣
<script type="text/javascript" src="main3d1cb903f77dad5737e9.js"></script>
打包出來的js文件是這樣
這樣就能解決這個問題了。
咱們不可能每次都去手動複製一個index.html到打包好的dist文件中,咱們會使用一款插件html-webpack-plugin
它能夠自動添加html文件到dist文件中,同時它會自動添加js文件並帶有hash值
引入插件
var HtmlWebpackPlugin = require('html-webpack-plugin');
使用插件
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.tmpl.html'), filename: 'index.html' })
這裏給你們解釋下,template是模板,咱們在不少狀況下,生產環境和開發環境不一樣,致使index.html引入的資源路徑不一樣,這是爲了改來改去,咱們能夠建立一個模板,它指定編譯時咱們copy的index.html文件。filename是最終生成的文件名。
模板文件以下
<!DOCTYPE html>
<html lang="zh"> <head> <meta charset="UTF-8"> <link rel="icon" href="favicon.ico"> <title>Projection-Web</title> </head> <body> <div id="root"></div> <script src="js/react.min.js"></script> <script src="js/react-dom.min.js"></script> </body> </html>
生成的index.html文件以下
<!DOCTYPE html>
<html lang="zh"> <head> <meta charset="UTF-8"> <link rel="icon" href="favicon.ico"> <title>Projection-Web</title> </head> <body> <div id="root"></div> <script src="js/react.min.js"></script> <script src="js/react-dom.min.js"></script> <script type="text/javascript" src="js/main3d1cb903f77dad5737e9.js"></script></body> </html>
下面是我打包編譯的dist文件夾內容
下面是生產環境的配置文件(部分)
var CopyWebpackPlugin = require("copy-webpack-plugin"); var HtmlWebpackPlugin = require('html-webpack-plugin'); var webpack = require("webpack"); var path = require('path'); var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { entry: "./src/index.tsx", output: { path: __dirname + "/dist", filename: "js/"+"[name][hash].js" }, devtool: "source-map", resolve: { extensions: [".ts", ".tsx", ".js", ".json"] }, module: { rules: [ { test: /\.tsx?$/, loader: "awesome-typescript-loader" }, { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }, { test: /\.(less|css)?$/, loader: ["style-loader", "css-loader", "less-loader", "postcss-loader"] }, { test: /\.(woff|svg|eot|ttf)?$/, loader: "url-loader" } ] }, externals: { "react": "React", "react-dom": "ReactDOM" }, plugins: [ new CopyWebpackPlugin([ { from: "node_modules/react/dist/react.js", to: "js/react.min.js" }, { from: "node_modules/react-dom/dist/react-dom.js", to: "js/react-dom.min.js" }, { from: "index.html", to: "index.html" }, { from: "public/favicon.ico", to: "favicon.ico" } ]), new HtmlWebpackPlugin({ template: path.join(__dirname, 'src/index.tmpl.html'), filename: 'index.html' }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] };
學好webpack,是一名現代前端開發工程師的基本素養。後續還會深刻webpack,謝謝你們