webpack與SPA實踐之開發環境搭建

歡迎訪問個人我的博客html

項目初始化

以你喜歡的任意方式,建立項目根目錄,如:vue

mkdir vue-hello

初始化包模塊管理文件

進入項目根目錄,初始化項目包模塊管理文件package.json:node

npm init

命令臺會提示你輸入一堆信息,若是你想簡單一點,能夠添加-y參數,跳過輸入步驟,生成默認信息:webpack

npm init -y

初始化項目package.json

初始化源碼目錄

在項目根目錄下建立源碼目錄結構,一般源碼目錄是src或app,我的喜愛是使用src:git

webpack簡述及使用

雖然在本篇文章咱們不會對webpack作太過詳細的介紹,可是依然但願能幫助讀者對webpack擁有更清晰的瞭解,webpack是什麼?github

webpack is a tool to build JavaScript modules in your applicationweb

webpack是一個幫助你的應用構建JavaScript模塊的工具。正則表達式

接下來,咱們介紹幾個知識點幫助理解webpack:模塊化,webpack原理及其與grunt和gulp的比較。express

模塊化

模塊化 指解決一個複雜問題時自頂向下逐層把系統劃分紅若干模塊的過程。npm

模塊是一個可組合、分解和更換的單元,將一個系統分解成若干模塊,單元;你們遵循必定的規範,各司其職,各自開發不一樣模塊;以後能夠較低成本的將模塊組合起來,構成一個完整的系統,極大方便了團隊成員之間的協助開發,產出效率獲得有效提高。

webpack原理

webpack是一個幫助你的應用構建JavaScript模塊的工具,其本質只能處理JavaScript,那你會疑惑了,不是說使用webpack,能夠很方便的在JavaScript代碼中引用圖片,CSS等資源嘛?是的,這正是webpack的優點,那怎麼實現的呢?這就要涉及到webpack中的一個概念:加載器(loader)

加載器 是做用於應用資源文件的轉換器,它們是一系列JavaScript函數,接受資源文件的內容作參數,而後返回新的資源(以一個JavaScript模塊的形式返回)。

因此,對於webpack,咱們明確三點:

  1. 模塊:webpack中一切資源文件(JavaScript, 樣式, 圖片資源等)皆視爲模塊;
  2. 加載器:webpack經過加載器(JavaScript函數)將其餘資源處理(構建)成JavaScript模塊;
  3. 管理依賴:webpack在編譯模塊時,就能分析查找該模塊內的依賴,能夠很好的處理不一樣模塊間的依賴;

webpack對比grunt/gulp

  • grunt

    打開grunt官網,你能夠看到最醒目的介紹:The JavaScript Task Runner,還有一個關鍵字automation - 自動化,其定位是一個JavaScript的自動化構建任務處理器,幫助開發者自動化處理項目的構建流程;

  • gulp

    gulp官網的定義是:Automate and enhance your workflow,自動強化項目構建流程,其與grunt的目標一致,都是幫助開發者自動化處理項目的構建流程,不一樣的是gulp實現方式是基於流的,即以流的方式處理文件,而grunt是以二進制方式處理文件,gulp使用性能是要強於grunt的;

  • 總結

    1. webpack定位是一個模塊化管理工具,而grunt/gulp都是自動化任務流程構建工具;
    2. grunt基於二進制處理文件,gulp基於流式處理文件,效率比grunt更高一些;
    3. webpack強大特性,使得其添加諸多插件能夠替代grunt/gulp,可是目前的實踐項目中,一般webpack結合gulp或grunt使用(各自處理各自專長的任務);

安裝

首先安裝webpack,npm或yarn均可以,無甚區別:

npm install --save-dev webpack

關於此處的--save-dev參數作簡要說明:

  1. --save是聲明將安裝依賴添加入package.json文件;
  2. 默認地,使用npm安裝包模塊依賴時,依賴關係存儲在在"dependencies"屬性對象內,表示項目依賴;
  3. webpack是做爲開發環境依賴的,不是做爲源碼直接調用,因此添加-dev參數,以聲明其是開發環境依賴;

webpack配置介紹

實踐項目使用webpack完成自動化構建,本地服務調試與熱加載,首先在根目錄下建立webpack的配置入口文件webpack.config.js,基本內容結構以下:

var path = require('path');

	module.exports = {
		context: path.resolve(__dirname, 'src/'),
    	entry: {
        	app: './scripts/app.js'
    	},
    	output: {
        	filename: '[name].js',
        	path: path.resolve(__dirname, 'dist/scripts/')
    	},
    	module: {
        	loaders: []
    	},
    	resolve: {
			modules:[],
        	alias: {},
        	extensions: []
    	},
		plugins: [],
		devServer: {}
	};

如上,webpack配置文件使用module.exports方式導出配置對象,webpack執行時會默認讀取項目根目錄下webpack.config.js文件,固然能夠手動配置指定一個文件做爲配置文件,咱們不討論,能夠參考webpack文檔,接下來對webpack配置內容作簡要介紹,若是你對webpack使用比較熟悉,能夠跳過此節。

文件處理配置

和webpack文件處理相關的幾個配置屬性主要有三個:目錄上下文信息(context), 處理文件入口信息(entry), 文件輸出信息(output)。

目錄上下文信息(context)

設置解析處理文件入口的相對目錄,值爲一個絕對目錄路徑,默認爲當前執行目錄,一般即項目根目錄,在Node中其值與process.cwd()相同,如:

context: path.resolve(__dirname, 'src/'),

如上即解析爲項目根目錄下的src目錄。

處理文件入口信息(entry)

處理文件入口,值能夠是字符串,或數組,或對象,值爲字符串或數組時,即輸出單文件,值爲對象,能夠輸出多文件,輸出文件名稱等信息參考文件輸出信息(output)。

文件輸出信息(output)

此配置聲明webpack編譯輸出文件的文件名等信息,如:

filename: '[name].js',

聲明文件名就是模塊(chunk)名,對應在entry中定義的入口,你可能還見過[id],[hash],[chunkhash]這些用法,現作簡單介紹:

  • [id]:該值表示webpack編譯模塊(chunk)的id,一般是一個數字;
  • [name]:該值表示webpack編譯模塊(chunk)名,對應entry中定義的入口名或文件名;
  • [chunkhash]:該值表示webpack編譯模塊(chunk)hash值,根據模塊內容計算出的一個md5值;
  • [hash]:該值表示webapck編譯對象hash值,根據編譯對象計算出的md5值;

編譯對象,即webpack執行時讀取配置後生成的一個編譯配置對象,包含模塊,待編譯文件,相對於上次編譯的變動文件等諸多信息,須要注意的是該對象在webpack啓動讀取配置文件後造成,在這次編譯過程保持不變。

  • output.path:定義輸出文件所在目錄;
  • output.publicPath:定義輸出文件在瀏覽器訪問時的基礎相對路徑,能夠與後文webpack-dev-server一塊兒使用。

webpack加載器與模塊

在介紹加載器配置以前,先看看加載器是什麼:

Loaders are transformations that are applied on a resource file of your app. They are functions (running in node.js) that take the source of a resource file as the parameter and return the new source.

加載器是做用於應用資源文件的轉換器,它們是一系列函數,接受資源文件的內容作參數,而後返回新的資源(以一個模塊的形式返回)。

webpack

webpack解析文件時使用的加載器都聲明在module屬性的loaders數組中,能夠設置一個或多個加載器。

  • module.noParse:指定某些文件不須要使用解析,支持傳入文件路徑或正則表達式;
  • module.loaders:指定解析文件的加載器模塊及各模塊解析規則,能夠設置如下屬性:
    1. test: 待解析文件需匹配的規則,一般以文件後綴名稱匹配文件;
    2. include:待解析文件所處目錄;
    3. exclude: 過濾掉的目錄;
    4. loader:加載器模塊名稱;
    5. loaders多個加載器;

模塊解析規則配置

webpack支持在resolve屬性對象中配置模塊解析規則,主要有rootmodules, aliasextensions屬性。

resolve.root與resolve.modules

該屬性設置的是在開發代碼中使用requireimport加載某模塊時,webpack查找該模塊所遵循的查找目錄範圍,如在源代碼中存在:

var utils = require('utils/utils.js');

root配置以下:

resolve: {
  		root: [
    		path.resolve('./src/'),
    		'node_modules'
  		]
	}

webpack編譯時將自動在項目根目錄下的src目錄內的utils目錄下查找utils.js,若存在,則返回,不然進入node_modules目錄內查找utils.js模塊。

固然,若未設置resolve.root屬性值,則webpack默認先從node_modules查找模塊,而後在根目錄下查找。

注意:resolve.root在webpack v1版本中使用,而在webpack v2 中,使用modules代替,建議使用v2版本,後文也將統一使用modules屬性。

resolve.alias

alias,有別名的意思,這裏的做用是設置其餘模塊或路徑的別名,webpack在解析模塊時,將使用配置值替換該別名,如,在未設置alias屬性內容時,即resolve.alias默認是空對象{}時,咱們在代碼中引用模塊:

var Vue = require('vue/dist/vue.js');
	var TopHeader = require('components/header.js');

webpack在編譯代碼時,按resolve.modules聲明的順序依次查找對應模塊,如按照上一節定義的resolve.modules

resolve: {
  		root: [
    		path.resolve('./src/'),
    		'node_modules'
  		]
	}

查找模塊時,將首先在src/vue/dist/目錄下尋找vue模塊,在src/components/目錄下查找header.js模塊,查找到後返回,不然進入node_modules目錄查找。

如今,咱們還能夠經過resolve.alias爲模塊或路徑聲明一個別名,更方便的聲明加載模塊:

resolve: {
		alias: {
            'vue$': 'vue/dist/vue.js'
            components: path.resolve(__dirname, 'src/components/')
        }
	}

我可使用以下方式加載模塊:

var Vue = require('vue');
	var TopHeader = require('components/header.js');
  • $符號

關於alias的詳細使用介紹,請參見文檔,本文並非要介紹webpack文檔,在這裏介紹一下聲明的vue$別名中的$符號:

這裏的$符合是正則的文末匹配符,即只有當vue是最後一級路徑時,webpack纔會將該值解析成別名,進行別名與對應值替換,如vue/test.js中的vue是不會看成別名解析的,而components/header.js中的components則會按照聲明的components別名進行解析,其結果是src/components/header.js

resolve.extensions

該值定義解析模塊時的查找文件的後綴順序,如["", ".js", ".json"],會優先返回js文件,其次json文件,而後是其餘文件,注意,這裏數組傳入了一個空字符串,他的做用是在未找到配置中全部列舉的類型文件時,支持webpack返回其餘類型文件。

webpack插件配置

webpack還支持多種多樣的插件拓展,你可使用它們對你的項目webpack模塊構建過程進行額外處理,如代碼壓縮,圖片等資源提交壓縮,構建異常捕獲和提高,構建流程時間消耗比,等等,而關於這些插件使用的配置在plugins屬性數組內,將在後續進行介紹。

項目本地調試與開發

爲了方便開發和調試,一般都須要在本地主機開啓服務,提供局域網內多終端訪問,而且在文件變動時,實時刷新頁面,正如grunt和gulp中Browsersync插件提供的功能同樣,webpack可使用webpack-dev-server模塊實現。

webpack-dev-server是一個Node.js的express服務器,以webpack開發中間件的形式爲webpack包提供服務,當監聽到源碼文件變動時,會自動從新打包,而且支持配置自動刷新瀏覽器,從新加載資源。

安裝

因爲該插件只用於開發模式,因此經過如下npm指令安裝:

npm install webpack-dev-server --save-dev

配置

啓用webpack-dev-server時,其默認開啓8080端口,訪問localhost:8080返回當前目錄下的index.html,即執行指令當前目錄下的靜態資源,固然能夠經過指令傳遞參數或在配置文件進行配置指定靜態資源目錄。

另外須要注意的是,開啓webpack-dev-server後,變動文件從新打包後,並不會實際輸出到配置的output目錄,而是在publicPath屬性聲明的相對路徑所在的內存中讀取。

靜態資源目錄配置(content base)

webpack-dev-server --content-base src/

執行以上指令開啓服務後,webpack-dev-server將默認在src/目錄返回靜態資源,固然也能夠在webpack.config.js配置文件中指定:

devServer: {
		contentBase: './src'
	}

訪問http://localhost:8080http://localhost:8080/index.html效果同樣,均返回src目錄下的index.html文件。

publicPath與輸出文件訪問

在前文webpack配置一節中提到output.publicPath屬性值表示,在瀏覽器訪問webpack output輸出的文件時的基礎相對路徑,如設置:

output: {
		path: 'dist/scripts',
		filename: 'app.js',
		publicPath: 'assets/'
	}

則在html文件中引用該app.js文件的方式以下:

<script src="assets/app.js"></script>

在瀏覽器中訪問app.js的方式爲:http://localhost:8080/assets/app.js

自動刷新(Automatic Refresh)

前面說到webpack-dev-server支持文件變動時,自動刷新瀏覽器,這也是開發者急需的功能,webpack-dev-server支持兩種方式實現:

  • iframe模式(iframe mode):頁面經過iframe窗口插入而後變動時自動從新加載;
  • inline模式(inline mode):經過sock.js(好比websocket協議,輪詢等方式)在頁面嵌入一個小型客戶端與webpack-dev-server服務器創建鏈接,當發生變動從新打包時,經過此鏈接通知頁面從新加載;
iframe模式

使用默認的iframe模式時,不須要進行任何配置,能夠直接訪問:http://localhost:8080/webpack-dev-server/index.html便可,如圖,經過iframe插入咱們的頁面:

iframe模式(iframe mode)

注意:

  • 在應用頁面上方會有狀態欄顯示當前應用信息;
  • 應用URL的變動發生在iframe內部,不會反映在瀏覽器地址欄;
inline模式

要開啓inline模式,只須要指定--inline命令行參數或在配置文件中指定inline: true

devServer: {
        contentBase: './src',
        inline: true
    }

在此模式下,直接訪問http://localhost:8080/index.htmlhttp://localhost:8080便可,此模式與iframe模式的區別在於:

  • 訪問URL不一樣;
  • 需指定inline配置參數;
  • 應用信息在控制檯輸出;
  • 應用URL的變動直接反映在瀏覽器地址欄;

其實inline模式還能夠配合Node.Js服務運行,以後再介紹,這裏只說明瞭其在HTML文檔中的使用。

熱替換(Hot Module Replacement)

除了自動刷新,webpack-dev-server的另外一大賣點是模塊熱替換,那麼熱替換究竟是什麼?

熱替換,即文件發生變動時,webpack包只替換髮生變動的模塊,而不須要所有替換;瀏覽器不須要徹底從新加載,而能夠直接將變動的模塊注入到運行中的應用。

開啓熱替換功能須要指定--hot指令參數,或在配置文件中添加:

devServer: {
        contentBase: './src',
        inline: true,
        hot: true
    }

注意到以上代碼,熱替換須要與inline模式一塊兒使用,另外須要指定output.publicPath值。

HotModuleReplacementPlugin

配置好後還須要使用webpack.HotModuleReplacementPlugin插件,才能真正啓用熱替換功能:

plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]

一樣的,熱替換能夠搭配Node.js服務一塊兒使用,以後介紹。

如圖,爲每一個文件(app.js, test.js)都實現了HMR熱替換,當發生變動時,只更新變動模塊,而不是從新加載全部文件:

熱替換

擁抱ES6

ES6推出以來,廣受Jser們青睞,其確實有不少新特性和新規範,值得咱們深刻學習並使用,能夠參考ECMAScript 6入門,將來全部的JavaScript應用都應該擁抱ES6,不過,目前各大瀏覽器都在推動ES6的實現,在兼容實現以前,咱們還須要過渡性的將其轉換成ES5語法,最通用的方式就是使用babel轉換。

使用babel加載器

首先,爲了能使用webpack和babel轉換js代碼,須要使用babel-loader加載器,另外還須要安裝babel轉換js的轉換規則插件,如babel-preset-es2015定義了轉換規則,安裝方式以下:

npm install --save-dev babel-loader babel-preset-es2015

而後在根目錄下建立.babelrc文件,內容:

{
    	"presets": ["es2015"]
	}

在webpack.config.js配置文件中,添加相關loader配置:

module: {
		loaders: [
			{
        		test: /\.(js|vue)$/,
        		exclude: /node_modules/,
        		loader: 'babel-loader'
    		}
		]
	}

其中,test匹配須要轉換的文件,exclude匹配不須要轉換的文件或目錄。

babel-polyfill

咱們須要明白的一點是babel-preset-es2015能作的,只是轉換ES6代碼成ES5,使得瀏覽器能夠解析執行,可是對於ES6新提出的API,如Promise,Generator等沒法簡單的轉換成ES5代碼,這時就須要babel-polyfill了,babel-polyfill是一個墊片,它能夠模擬提供全部的ES6功能和特性,能夠看做是提供了一個模擬的全局ES6環境。

  • 安裝

安裝依然很簡單,因爲是墊片,是須要在源碼中使用的,全部指定--save參數:

npm install --save babel-polyfill
  • 使用

不一樣於babel-preset-es2015babel-polyfill須要直接打包進源碼,並且須要在使用ES6代碼前引入一次:

import 'babel-polyfill'

只須要引入一次,由於該墊片提供的是一個模擬的全局ES6環境,而不是模塊內部的。

或者直接在webpack.config.js中配置打包入口文件時加入該墊片:

entry: {
        app: ['babel-polyfill', './scripts/app.js']
    },

如今你能夠在你的代碼中使用任何你想用的ES6 API了。

到此,基本介紹瞭如何使用webpack搭建開發環境,下一篇將開始介紹如何使用webpack處理CSS及如何使用Vue進行組件化開發。

相關文章
相關標籤/搜索