如何學習配置webpack(一)

項目小白如何從0開始配置webpack

本身配置過webpack的人應該都知道,webpack真的好複雜,一開始作項目都是拿別人現成的作作小修改,可是別人的終究沒有本身配的舒服。因此我打算寫這篇文章,從個人配置中帶你們瞭解webpack配置,簡化讀webpack官方頁面的複雜度。javascript

首先咱們須要明確咱們要求webpack作什麼?css

1.所寫即所得,咱們在編譯器中寫了代碼能立刻呈如今調試器上(熱更新服務)html

2.本地開發獲取數據存在的跨域問題(代理,解決跨域)vue

3.使用es678,less,sass等(翻譯,讓瀏覽器懂得咱們的代碼)java

4.提升項目性能,好比壓縮代碼,壓縮圖片等(項目優化)node

5.若是與輸入相關的需求,找entry(好比多頁面就有多個入口) 6.若是與輸出相關的需求,找output(好比你須要定義輸出文件的路徑、名字等等) 7.若是與模塊尋址相關的需求,找resolve(好比定義別名alias) 8.若是與轉譯相關的需求,找loader(好比處理sass處理es678N) 9.若是與構建流程相關的需求,找plugin(好比我須要在打包完成後,將打包好的文件複製到某個目錄,而後提交到git上) 接下來咱們來看下webpack的一些基本配置react

1.entry(項目入口)webpack

2.output(出口文件)git

3.modules(模塊處理)es6

4.plugin(插件)

5.resolve

6.devserver

7.mode,

8devtool

1.項目入口設置(entry)

主要有三種方式

1.字符串形式

entry: '.src/main.js'
複製代碼

2.數組形式

entry: [react, react-dom]
複製代碼

3.對象形式

entry: {
    main:'./src/index2.js',
    second: './src/index2.js',
    vendor:['react', 'react-dom']
}
複製代碼

2.出口文件設置及(output)

const path = require('path');
module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};
複製代碼

在上面的示例中,咱們經過 output.filename 和 output.path 屬性,來告訴 webpack bundle 的名稱,以及咱們想要 bundle 生成(emit)到哪裏。 Path.resolve是什麼,引入的path模塊是幹什麼用的

Nodejs

該path.resolve()方法將一系列路徑或路徑段解析爲絕對路徑。

給定的路徑序列從右到左處理,隨後每一個path路徑都被預先加載,直到構造出絕對路徑。例如,給定路徑段的序列:/foo,/bar,baz,調用path.resolve('/foo', '/bar', 'baz')將返回/bar/baz。

若是在處理path完全部給定段以後還沒有生成絕對路徑,則使用當前工做目錄。 生成的路徑已規範化,而且除非將路徑解析爲根目錄,不然將刪除尾部斜槓。 零長度path段被忽略。

若是沒有path傳遞段,path.resolve()將返回當前工做目錄的絕對路徑。

path.resolve('/foo/bar', './baz');
// Returns: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/');
// Returns: '/tmp/file'
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// If the current working directory is /home/myself/node,
// this returns '/home/myself/node/wwwroot/static_files/gif/image.gif'
_dirname
複製代碼

當前模塊的目錄名稱。這是同樣 path.dirname()的__filename。

示例:node example.js從中運行/Users/mjr

console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
複製代碼

咱們在配置output是經常使用的幾個

filename 這個是輸出文件的名稱,字符串類型,若是隻有一個輸出文件,能夠寫成靜態名稱。例如

output:{
     filename:'bundle.js'
}
複製代碼

固然了,在咱們平常工做中,通常狀況下是不會有這種狀況的,當項目很大的時候,若是不分塊打包,bundle.js會驚人的大,項目越大,bundle.js就會越大,這不是咱們今天討論的重點,之後再說

多個chunk的時候怎麼辦呢

webpack會爲每一個生成的Chunk取一個名稱,Chunk的名稱和Entry的配置有關:

1. 若是entry是一個string或者array,就只會生成一個chunk,這個chunk的名稱是main;

2. 若是entry是一個object,就可能出現多個chunk,這時chunk的名稱是object鍵值對裏鍵的名稱

然而,當經過多個入口起點(entry point)、代碼拆分(code splitting)或各類插件(plugin)建立多個

bundle,應該使用如下一種替換方式,來賦予每一個 bundle 一個惟一的名稱…… 使用入口名稱:

output:{
     filename: "[name].bundle.js"
}
複製代碼

使用內部 chunk id

output:{
    filename: "[id].bundle.js"
}
複製代碼

使用每次構建過程當中,惟一的 hash 生成

output:{
filename: "[name].[hash].bundle.js"
}
複製代碼

使用基於每一個 chunk 內容的 hash:

output:{
filename: "[chunkhash].bundle.js"
}
複製代碼

這裏多出來幾個陌生詞彙hash、chunkhash,它們是什麼? hash、chunkhash和contenthash三者的區別 hash

hash的值是相同的,若是都使用hash的話,由於這是工程級別的,即每次修改任何一個文件,全部文件名的hash至都將改變。因此一旦修改了任何一個文件,整個項目的文件緩存都將失效。因此對於沒有改變的模塊而言,這樣作顯然不恰當,由於緩存失效了嘛。此時,chunkhash的用途隨之而來。 chunkhash

只有被修改了的文件的文件名,hash值修改

filename: '[name]-[chunkhash].js'

當咱們使用mini-css-extract-plugin拆分css的時候,就須要使用chunkhash,我一個js文件裏面引入了css文件。這時要是我修改了js,但沒修改css,能夠經過chunkhash緩存css文件

contenthash

對css使用了chunkhash以後,咱們測試會發現,若是修改了js,css文件名的hash值確實沒變,但這時要是咱們修改css文件的話,咱們就會發現css文件名的chunkhash值竟然沒變化,這樣就致使咱們的非覆蓋發佈css文件失效了。因此這裏須要注意就是css文件必須使用contenthash。

上面介紹的 id、name、hash、chunkhash等都是webpack內置變量, id是惟一標示,不會重複,從0開始, name 是模塊名稱,是你本身起的,在配置路由懶加載的時候能夠本身命名 官網介紹的很清楚,我就再也不這裏囉嗦了, chunkFilename

官網解釋:此選項決定了非入口(non-entry) chunk 文件的名稱, 什麼場景須要呢?

在按需加載(異步)模塊的時候,也就是路由懶加載,這樣的文件是沒有被列在entry中的,

好比

{
    entry: {
        "index": "pages/index.jsx"
    },
    output: {
         filename: "[name].min.js",
        chunkFilename: "[name].min.js"
    }
}
const myModel = r => require.ensure([], () => r(require('./myVue.vue')), 'myModel')
複製代碼

上面的例子,經過filename輸出的是index.min.js 異步加載的模塊是要以文件形式加載哦,因此這時生成的文件名是以chunkname配置的,經過chunkFilename輸出的是myModel.min.js 因此chunkFilename也很重要哦!!! path path是配置輸出文件存放在本地的目錄,字符串類型,是絕對路徑

output:{
    path: path.resolve(__dirname, 'dist/assets')
}
複製代碼

__dirname,這個昨天說過,能夠回顧一下,就是當前文件所在的文件夾的名字 publicPath 對構建出的資源進行異步加載(圖片,文件),該選項的值是以 runtime(運行時) 或 loader(載入時) 所建立的每一個 URL 爲前綴。所以,在多數狀況下,此選項的值都會以/結束。 默認值是一個空字符串 "",即相對路徑,配置錯誤會致使404 簡單說,就是靜態文件託管在cdn上 舉個栗子: 若是你這麼配置:

output:{
    filename:'[name]_[chunkhash:8].js',
    publicPath:'https://www.qdtalk.com/assets/'
}
複製代碼

打包編譯後,html頁面就是這樣的

path 和publicPath都支持字符串模板

  • 配置 單出口 // webpack 配置
const path = require('path');

module.exports = {
  entry: main: './src/main.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
};
複製代碼
  • 配置 多出口
// webpack 配置
const path = require('path');

module.exports = {
  entry: {
    app: './src/app.js',
    vendors: './src/vendors.js'
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
};
複製代碼

3.modules(模塊處理)

模塊處理主要是對loader的配置

此處引用官網對loader的定義

loader 讓 webpack 可以去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 能夠將全部類型的文件轉換爲webpack可以處理的有效模塊,而後你就能夠利用webpack的打包能力,對它們進行處理。

本質上,webpack loader 將全部類型的文件,轉換爲應用程序的依賴圖(和最終的 bundle)能夠直接引用的模塊。 注意,loader 可以 import 導入任何類型的模塊(例如 .css 文件),這是 webpack 特有的功能,其餘打包程序或任務執行器的可能並不支持。咱們認爲這種語言擴展是有很必要的,由於這可使開發人員建立出更準確的依賴關係圖。 在更高層面,在 webpack 的配置中 loader 有兩個目標:

1. test 屬性,用於標識出應該被對應的 loader 進行轉換的某個或某些文件。

2. use 屬性,表示進行轉換時,應該使用哪一個 loader。

webpack.config.js
const path = require('path');
const config = {
  output: {
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  }
};
module.exports = config;
複製代碼

以上配置中,對一個單獨的 module 對象定義了 rules 屬性,裏面包含兩個必須屬性:test 和 use。這告訴 webpack 編譯器(compiler) 以下信息:

「嘿,webpack 編譯器,當你碰到「在 require()/import 語句中被解析爲 '.txt' 的路徑」時,在你對它打包以前,先使用 raw-loader 轉換一下。」 重要的是要記得,在 webpack 配置中定義 loader 時,要定義在 module.rules 中,而不是 rules。然而,在定義錯誤時 webpack 會給出嚴重的警告。爲了使你受益於此,若是沒有按照正確方式去作,webpack 會「給出嚴重的警告」

在webpack中有許許多多的loader,此處我按照官方文檔解釋下和我理解的用法 loader 特性

  • 幾乎全部 loader 都 須要安裝, 但 不須要 在 webpack 配置文件中經過 require 引入
  • 逆向編譯,鏈式傳遞 文件
  • raw-loader 加載文件原始內容(utf-8)
  • val-loader 將代碼做爲模塊執行,並將 exports 轉爲 JS 代碼
  • url-loader 像 file loader 同樣工做,但若是文件小於限制,能夠返回 data URL
  • file-loader 將文件發送到輸出文件夾,並返回(相對)URL raw-loader 可讓你在代碼中引入文件
import txt from './file.txt';
複製代碼

val-loader 加載的模塊必須使用如下函數接口,將 default export 導出爲一個函數。

function answer () {
  return {
    code: 'module.exports = 42;'
  }
};
module.exports = answer;
複製代碼

url-loader

url-loader 功能相似於 file-loader,可是在文件大小(單位 byte)低於指定的限制時,能夠返回一個 DataURL。 file-loader 將文件發送到輸出文件夾,並返回(相對)URL(不會再對文件作處理) JSON

  • json-loader 加載 JSON 文件(默認包含)
  • json5-loader 加載和轉譯 JSON 5 文件
  • cson-loader 加載和轉譯 CSON 文件 主要處理json文件

轉換編譯(Transpiling)

  • script-loader 在全局上下文中執行一次 JavaScript 文件(如在 script 標籤),不須要解析
  • babel-loader 加載 ES2015+ 代碼,而後使用 Babel 轉譯爲 ES5
  • buble-loader 使用 Bublé 加載 ES2015+ 代碼,而且將代碼轉譯爲 ES5
  • traceur-loader 加載 ES2015+ 代碼,而後使用 Traceur 轉譯爲 ES5
  • ts-loader 或 awesome-typescript-loader 像 JavaScript 同樣加載 TypeScript 2.0+
  • coffee-loader 像 JavaScript 同樣加載 CoffeeScript

React開發過程當中咱們須要將jsx或者es6代碼轉譯成es5代碼。 咱們須要用到babel-loader

{
'test': /\.(js|jsx)$/, // babel 轉換爲兼容性的 js
'exclude': /node_modules/,
'loader': 'babel-loader',
'query': {
'presets': ['react', 'latest', 'stage-0', 'react-hmre']
},
'include': path.resolve(__dirname, '../client')
},
複製代碼

注:exclude是不轉換node_modules的代碼,query === options 此處注意babel版本6和7設置上有區別。 若是引用錯誤會報錯 模板(Templating)

  • html-loader 導出 HTML 爲字符串,須要引用靜態資源
  • pug-loader 加載 Pug 模板並返回一個函數
  • jade-loader 加載 Jade 模板並返回一個函數
  • markdown-loader 將 Markdown 轉譯爲 HTML
  • react-markdown-loader 使用 markdown-parse parser(解析器) 將 Markdown 編譯爲 React 組件
  • posthtml-loader 使用 PostHTML 加載並轉換 HTML 文件
  • handlebars-loader 將 Handlebars 轉移爲 HTML
  • markup-inline-loader 將內聯的 SVG/MathML 文件轉換爲 HTML。在應用於圖標字體,或將 CSS 動畫應用於 SVG 時很是有用。

樣式

  • style-loader 將模塊的導出做爲樣式添加到 DOM 中
  • css-loader 解析 CSS 文件後,使用 import 加載,而且返回 CSS 代碼
  • less-loader 加載和轉譯 LESS 文件
  • sass-loader 加載和轉譯 SASS/SCSS 文件
  • postcss-loader 使用 PostCSS 加載和轉譯 CSS/SSS 文件
  • stylus-loader 加載和轉譯 Stylus 文件 若是咱們在開發中使用了less或者sass,咱們須要先轉換成css在引入到項目中
{
'test': /\.less$/,
'loader': ['style-loader', 'css-loader', {
'loader': 'less-loader',
'options': {
'javascriptEnabled': true
}
}]
},
複製代碼

此處的執行順序。less-loader -> css-loader -> style-loader 清理和測試(Linting && Testing)

  • mocha-loader 使用 mocha 測試(瀏覽器/NodeJS)
  • eslint-loader PreLoader,使用 ESLint 清理代碼
  • jshint-loader PreLoader,使用 JSHint 清理代碼
  • jscs-loader PreLoader,使用 JSCS 檢查代碼樣式
  • coverjs-loader PreLoader,使用 CoverJS 肯定測試覆蓋率

框架(Frameworks)

  • vue-loader 加載和轉譯 Vue 組件
  • polymer-loader 使用選擇預處理器(preprocessor)處理,而且 require() 相似一等模塊(first-class)的 Web 組件
  • angular2-template-loader 加載和轉譯 Angular 組件

核心重點(敲黑板)

plugins(插件)

做用:能夠處理各類任務,從打包優化和壓縮,一直到從新定義環境中的變量 loader不須要require. plugin須要

loader 被用於轉換某些類型的模塊,而插件則能夠用於執行範圍更廣的任務。插件的範圍包括,從打包優化和壓縮,一直到從新定義環境中的變量。插件接口功能極其強大,能夠用來處理各類各樣的任務。

想要使用一個插件,你只須要 require() 它,而後把它添加到 plugins 數組中。多數插件能夠經過選項(option)自定義。你也能夠在一個配置文件中由於不一樣目的而屢次使用同一個插件,這時須要經過使用 new 操做符來建立它的一個實例。 下面是我配置的用於開發環境的plugins實例

webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 經過 npm 安裝
const webpack = require('webpack'); // 用於訪問內置插件
const config = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};
module.exports = config;
複製代碼
'plugins': [
new webpack.optimize.OccurrenceOrderPlugin(), // 調整模塊的打包順序,用到次數更多的會出如今文件的前面
new webpack.DefinePlugin({ // DefinePlugin 容許建立一個在編譯時能夠配置的全局常量。
'process.env.NODE_ENV': JSON.stringify('development')
}),
new HtmlWebpackPlugin({ // HtmlWebpackPlugin簡化了HTML文件的建立,以便爲你的webpack包提供服務。這對於在文件名中包含每次會隨着編譯而發生變化哈希的 webpack bundle 尤爲有用。 你可讓插件爲你生成一個HTML文件,使用lodash模板提供你本身的模板,或使用你本身的loader。
'hash': true,
'title': 'Demo',
'filename': 'index.html',
'template': path.resolve(__dirname, '../views/index.ejs'),
'inject': 'body'
}),
new webpack.HotModuleReplacementPlugin(), // 啓用熱更新
new webpack.NoEmitOnErrorsPlugin(), // 輸出階段遇到編譯錯誤跳過
new webpack.NamedModulesPlugin(), // 當開啓 HMR 的時候使用該插件會顯示模塊的相對路徑,建議用於開發環境。
new webpack.ProgressPlugin(), // 輸出構建進度
]
複製代碼
相關文章
相關標籤/搜索