從0到1搭建webpack2+vue2自定義模板詳細教程

前言

webpack2和vue2已經不是新鮮東西了,滿大街的文章在講解webpack和vue,可是不少內容寫的不是很詳細,對於不少個性化配置仍是須要本身過一遍文檔。Vue官方提供了多個vue-templates,基於vue-cli用官方的webpack模板居多,不過對於不少人來講,官方的webpack模板的配置仍是過於複雜,對於咱們瞭解細節實現不是很好,因此想本身從零開始搭建一個模板工程,也順便從新認識一下webpack和vue工程化的細節。javascript

webpack 核心概念

Webpack 是當下最熱門的前端資源模塊化管理和打包工具。它能夠將許多鬆散的模塊按照依賴和規則打包成符合生產環境部署的前端資源。還能夠將按需加載的模塊進行代碼分隔,等到實際須要的時候再異步加載。經過 loader 的轉換,任何形式的資源均可以視做模塊,好比 CommonJs 模塊、 AMD 模塊、 ES6 模塊、CSS、圖片、 JSON、Coffeescript、 LESS 等。css

官方網站:https://webpack.js.org/html

安裝

在開始前,先要確認你已經安裝Node.js的最新版本。使用 Node.js 最新的 LTS 版本,是理想的起步。使用舊版本,你可能遇到各類問題,由於它們可能缺乏 webpack 功能或缺乏相關 package 包。前端

本地局部安裝:vue

# 安裝 latest release
npm install --save-dev webpack
# 簡寫模式
npm install -D webpack
# 安裝特定版本
npm install --save-dev webpack@<version>

全局安裝:html5

npm install -g webpack

注意:不推薦全局安裝 webpack。這會鎖定 webpack 到指定版本,而且在使用不一樣的 webpack 版本的項目中可能會致使構建失敗。可是全局安裝能夠在命令行調用 webpack 命令。java

【補充】npm install 安裝模塊參數說明:node

-g, --global 全局安裝(global)
-S, --save 安裝包信息將加入到dependencies(生產階段的依賴)
-D, --save-dev 安裝包信息將加入到devDependencies(開發階段的依賴),因此開發階段通常使用它
-O, --save-optional 安裝包信息將加入到optionalDependencies(可選階段的依賴)
-E, --save-exact 精確安裝指定模塊版本

npm 相關的更多命令參考這篇文章:npm 經常使用命令詳解webpack

而後在根目錄下建立一個 webpack.config.js 文件後,你能夠經過配置定義webpack的相關操做。git

入口(Entry)

入口起點告訴 webpack 從哪裏開始,並遵循着依賴關係圖表知道要打包什麼。能夠將您應用程序的入口起點認爲是根上下文(contextual root)或 app 第一個啓動文件。

單個入口(簡寫)語法:
用法:entry: string|Array<string>

webpack.config.js:

module.exports = {
  entry: './src/main.js'
};

對象語法:
用法:entry: {[entryChunkName: string]: string|Array<string>}

webpack.config.js:

module.exports = {
  entry: {
    app: './src/main.js',
    vendor: ['vue']
  }
};

這裏咱們將vue做爲庫vendor打包,業務邏輯代碼做爲app打包,實現了多個入口,同時也能夠將多個頁面分開打包。

多頁面應用程序一般使用對象語法構建。對象語法是「可擴展的 webpack 配置」,可重用而且能夠與其餘配置組合使用。這是一種流行的技術,用於將關注點(concern)從環境(environment)、構建目標(build target)、運行時(runtime)中分離。而後使用專門的工具(如webpack-merge)將它們合併。

注:vue-cli 生成的模板中build文件夾下有四個配置文件:

後三個文件經過webpack-merge插件合併了基本配置,將不一樣環境下的配置拆分多個文件,這樣更加方便管理。

出口(Output)

將全部的資源(assets)歸攏在一塊兒後,還須要告訴 webpack 在哪裏打包應用程序。webpack 的 output 屬性描述瞭如何處理歸攏在一塊兒的代碼(bundled code)。output 選項控制 webpack 如何向硬盤寫入編譯文件。注意,即便能夠存在多個入口起點,但只指定一個輸出配置。

在 webpack 中配置output 屬性的最低要求是,將它的值設置爲一個對象,包括如下兩點:

  • output.filename:編譯文件的文件名;

  • output.path對應一個絕對路徑,此路徑是你但願一次性打包的目錄。

單個入口:

const path = require('path');
module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'build')  //__dirname + '/build'
  }
}

多個入口:
若是你的配置建立了多個 "chunk"(例如使用多個入口起點或使用相似CommonsChunkPlugin 的插件),你應該使用如下的替換方式來確保每一個文件名都不重複。

  • [name] 被 chunk 的 name 替換。

  • [hash] 被 compilation 生命週期的 hash 替換。

  • [chunkhash] 被 chunk 的 hash 替換。

const path = require('path');
module.exports = {
  entry: {
    app: './src/main.js',
    vendor: ['vue']
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'build')
  }
}

// 寫入到硬盤:./build/app.js, ./build/vendor.js

加載器(Loaders)

loader 用於對模塊的源代碼進行轉換。loader 可使你在 require() 或"加載"模塊時預處理文件。所以,loader 相似於其餘構建工具中「任務(task)」,並提供了處理前端構建步驟的強大方法。loader 能夠將文件從不一樣的語言(如 TypeScript)轉換爲 JavaScript,或將內聯圖像轉換爲 data URL。loader 甚至容許你在 JavaScript 中 require() CSS文件!

在你的應用程序中,有三種方式使用 loader:

這裏咱們主要說明一下使用webpack.config.js配置,使用loader須要在module的rules下配置相應的規則,以css-loader的webpack.config.js爲例說明:

module.exports = { 
    module: { 
        rules: [
            {test: /\.css$/, use: 'css-loader'}
        ]
    }
};

這三種配置方式等效:

{test: /\.css$/, use: 'css-loader'}
{test: /\.css$/, loader: 'css-loader',options: { modules: true }}
{test: /\.css$/, use: {
    loader: 'css-loader',
    options: {
      modules: true
    }
}}

注:loader/query能夠和options能夠在同一級使用,可是不要使用use和options在同一級使用。

CSS樣式分離

爲了用 webpack 對 CSS 文件進行打包,你能夠像其它模塊同樣將 CSS 引入到你的 JavaScript 代碼中,同時用css-loader(像 JS 模塊同樣輸出 CSS),也能夠選擇使用ExtractTextWebpackPlugin(將打好包的 CSS 提出出來並輸出成 CSS 文件)。

引入 CSS:

import 'bootstrap/dist/css/bootstrap.css';

安裝css-loader和style-loader:

npm install --save-dev css-loader style-loader

在 webpack.config.js 中配置以下:

module.exports = {
    module: {
        rules: [{
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
        }]
    }
}

資源路徑處理

由於.png等圖片文件不是一個 JavaScript 文件,你須要配置 Webpack 使用file-loader或者url-loader去處理它們。使用它們的好處:

  • file-loader 能夠指定要複製和放置資源文件的位置,以及如何使用版本哈希命名以得到更好的緩存。此外,這意味着 你能夠就近管理你的圖片文件,可使用相對路徑而不用擔憂佈署時URL問題。使用正確的配置,Webpack 將會在打包輸出中自動重寫文件路徑爲正確的URL。

  • url-loader 容許你有條件將文件轉換爲內聯的 base-64 URL(當文件小於給定的閾值),這會減小小文件的 HTTP 請求。若是文件大於該閾值,會自動的交給 file-loader 處理。

安裝 file-loader 和 url-loader:

npm install --save-dev file-loader url-loader

配置說明:

{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url-loader',
    options: {
        limit: 10000,
        name: 'img/[name]_[hash:7].[ext]'
    }
},
{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    loader: 'url-loader',
    options: {
        limit: 10000,
        name: 'fonts/[name].[hash:7].[ext]'
    }
}

插件(Plugins)

因爲 loader 僅在每一個文件的基礎上執行轉換,而插件(plugins)最經常使用於(但不限於)在打包模塊的「compilation」和「chunk」生命週期執行操做和自定義功能(查看更多)。webpack 的插件系統極其強大和可定製化

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

生產環境構建

對於Vue生產環境構建過程當中壓縮應用代碼和使用Vue.js 指南 - 刪除警告去除 Vue.js 中的警告,這裏咱們參考vue-loader文檔中的配置說明:

if (process.env.NODE_ENV === 'production') {
    // http://vue-loader.vuejs.org/zh-cn/workflow/production.html
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: false,
            compress: {
                warnings: false
            }
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true
        })
    ])
}

顯然咱們不想在開發過程當中使用這些配置,因此這裏咱們須要使用環境變量動態構建,咱們也可使用兩個分開的 Webpack 配置文件,一個用於開發環境,一個用於生產環境,相似於vue-cli中使用 webpack-merge 合併配置的方式。

可使用 Node.js 模塊的標準方式:在運行 webpack 時設置環境變量,而且使用 Node.js 的process.env來引用變量。NODE_ENV變量一般被視爲事實標準(查看這裏)。使用cross-env包來跨平臺設置(cross-platform-set)環境變量。

安裝cross-env:

npm install --save-dev cross-env

設置package.json中的scripts字段:

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
}

這裏咱們使用了cross-env插件,cross-env使得你可使用單個命令,而無需擔憂爲平臺正確設置或使用環境變量。

模塊熱替換

模塊熱替換功能會在應用程序運行過程當中替換、添加或刪除模塊,而無需從新加載頁面。這使得你能夠在獨立模塊變動後,無需刷新整個頁面,就能夠更新這些模塊,極大地加速了開發時間。

這裏咱們使用webpack-dev-server插件,webpack-dev-server 爲你提供了一個服務器和實時重載(live reloading)功能。webpack-dev-server是一個小型的node.js Express服務器,它使用webpack-dev-middleware中間件來爲經過webpack打包生成的資源文件提供Web服務。它還有一個經過Socket.IO鏈接着webpack-dev-server服務器的小型運行時程序。webpack-dev-server發送關於編譯狀態的消息到客戶端,客戶端根據消息做出響應。

安裝 webpack-dev-server:

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

安裝完成以後,你應該可使用 webpack-dev-server 了,方式以下:

webpack-dev-server --open

上述命令應該自動在瀏覽器中打開 http://localhost:8080

webpack.config.js配置:

module.exports = {
    ...
    devServer: {
        historyApiFallback: true, // 任意的 404 響應都替代爲 index.html
        hot: true, // 啓用 webpack 的模塊熱替換特性
        inline: true // 啓用內聯模式
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
    ...
}

更多的配置說明能夠看文檔:DevServer

動態生成 html 文件

該插件將爲你生成一個HTML5文件,其中包括使用script標籤的body中的全部webpack包,也就是咱們不須要手動經過script去引入打包生成的js,特別是若是咱們生成的文件名是動態變化的,使用這個插件就能夠輕鬆的解決,只需添加插件到您的webpack配置以下:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    ...
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'index.html',
            inject: true
        })
    ]
    ...
}

提取 CSS 文件

extract-text-webpack-plugin是一個 能夠將 *.vue 文件內的 <style> 提取,以及JavaScript 中導入的 CSS 提取爲單個 CSS 文件。配置文檔具體見這裏:extract-text-webpack-plugin

安裝:

npm install --save-dev extract-text-webpack-plugin

配置:

const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader"
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("styles.css"),
  ]
}

同時支持咱們能夠配置生成多個css文件,這樣咱們能夠將業務邏輯代碼和引用的樣式組件庫分離。

const ExtractTextPlugin = require('extract-text-webpack-plugin');

// Create multiple instances
const extractCSS = new ExtractTextPlugin('stylesheets/[name]-one.css');
const extractLESS = new ExtractTextPlugin('stylesheets/[name]-two.css');

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: extractCSS.extract([ 'css-loader', 'postcss-loader' ])
      },
      {
        test: /\.less$/i,
        use: extractLESS.extract([ 'css-loader', 'less-loader' ])
      },
    ]
  },
  plugins: [
    extractCSS,
    extractLESS
  ]
};

clean-webpack-plugin

在編譯前,刪除以前編譯結果目錄或文件:

npm install --save-dev clean-webpack-plugin

配置:

plugins: [
    new CleanWebpackPlugin(['dist'])
]

這樣當咱們在構建的時候能夠自動刪除以前編譯的代碼。

解析(Resolve)

這些選項能設置模塊如何被解析。webpack 提供合理的默認值,可是仍是可能會修改一些解析的細節。

resolve: {
  alias: {
    'vue$': 'vue/dist/vue.esm.js',
    '@': path.join(__dirname, 'src')
  },
  extensions: ['.js', '.json', '.vue', '.css']
}

咱們使用最多的就是別名(alias)和自動解析肯定的擴展(extensions),例如上面的@能夠代替項目中src的路徑,例如:

import tab from '@/components/tab.vue'

咱們引用src/components目錄下的tab.vue組件,不須要經過../之類的計算文件相對路徑。這裏的extensions可讓咱們在引入模塊時不帶擴展:

import tab from '@/components/tab'

至此咱們已經學習了咱們項目devDependencies依賴中經常使用的模塊:

webpack 
css-loader / style-loader
file-loader / url-loader 
cross-env 
webpack-dev-server 
html-webpack-plugin 
extract-text-webpack-plugin
clean-webpack-plugin

這裏咱們只說明瞭css、圖片、html模板資源webpack相關的加載器和插件,對於js相關的內容絲毫沒有提到,顯然這是不合乎情理的。之因此要把js單獨拿出來是由於js相關的內容很重要,獨立出來詳細去概括一下更合適。


webpack 中如何使用 es6 ~ es8?

做爲一個前端,相信 es6 幾乎是無人不知,不少人也必定知道可使用Babel作語法轉換,可是對於Babel有哪一些版本,每一個版本支持的es6語法有哪一些應該不是全部人都清楚的,這就是這部份內容要寫的意義。畢竟若是咱們的插件只用到了es6中的沒一些新特性,爲此將整個包引入就有點不太合適,另外爲了更好的用上新特性,咱們至少要明白有哪一些新特性吧。

ECMAScript 標準創建的過程

ECMAScript 和 JavaScript 的關係在此再也不贅述,建議閱讀一下阮一峯老師的《ECMAScript 6簡介》,咱們須要瞭解的是從ECMAScript 2016開始,ECMAScript將進入每一年發佈一次新標準的階段。制定ECMAScript 標準的組織是ECMAScript TC39TC39(ECMA技術委員爲39)是推進JavaScript發展的委員會。 它的成員是都是企業(主要是瀏覽器廠商)。TC39會按期的開會, 會議的主要成員時是成員公司的表明,以及受邀請的專家。

一種新的語法從提案到變成正式標準,須要經歷五個階段。每一個階段的變更都須要由 TC39 委員會批准。

  • Stage 0 - Strawman(展現階段)

  • Stage 1 - Proposal(徵求意見階段)

  • Stage 2 - Draft(草案階段)

  • Stage 3 - Candidate(候選人階段)

  • Stage 4 - Finished(定案階段)

建議看一下alinode 團隊的圖說ECMAScript新標準(一)就能夠大體瞭解整個過程。

安裝 Babel

Babel 如今的官網提供了一個能夠根據你的工具提示下載合適的包,具體見這裏:Using Babel

若是你想要在命令行使用Babel,你能夠安裝babel-cli,可是全局的安裝babel-cli不是一個好的選擇,由於這樣限定了你Babel的版本;若是你須要在一個Node項目中使用Babel,你可使用babel-core。

咱們這裏天然選擇webpack構建咱們的工程,下載方案以下:

npm install --save-dev babel-loader babel-core

而後咱們須要在項目根目錄下創建.babelrc文件:

{
  "presets": [],
  "plugins": []
}

注:在window下沒法經過 右鍵=>新建 命令來建立以點開頭的文件和文件夾,咱們能夠經過下面的命令生成.babelrc文件:

type NUL > .babelrc

Linux和Mac下能夠經過touch命令生成:

touch .babelrc

Babel 預設(presets)

Babel是一個編譯器。 在高層次上,它有3個階段,它運行代碼:解析,轉換和生成(像許多其餘編譯器)。默認狀況下,Babel 6並無攜帶任何轉換器,所以若是對你的代碼使用Babel的話,它將會原文輸出你的代碼,不會有任何的改變。所以你須要根據你須要完成的任務來單獨安裝相應的插件。

你能夠經過安裝插件(plugins)或預設(presets,也就是一組插件)來指示 Babel 去作什麼事情。Babel 提供了多個版本的官方預設:

babel-preset-env

babel-preset-env能夠根據你配置的選項,自動添加一些其餘的轉換器,來知足你當前的裝換需求。.babelrc文件新增了options選項:

{
  "presets": ["env", options]
}

具體的配置內容:

  • targets.node 支持到哪一個版本的 node

  • targets.browsers 支持到哪一個版本的瀏覽器

  • loose 啓動寬鬆模式,配合 webpack 的 loader 使用

  • modules 使用何種模塊加載機制

  • debug 開啓調試模式

  • include 包含哪些文件

  • exclude 排除哪些文件

  • useBuiltIns 是否對 babel-polyfill 進行分解,只引入所需的部分

babel-preset-es2015

es2015(ES6)相關方法轉譯使用的插件,具體見文檔

  • check-es2015-constants // 檢驗const常量是否被從新賦值

  • transform-es2015-arrow-functions // 編譯箭頭函數

  • transform-es2015-block-scoped-functions // 函數聲明在做用域內

  • transform-es2015-block-scoping // 編譯const和let

  • transform-es2015-classes // 編譯class

  • transform-es2015-computed-properties // 編譯計算對象屬性

  • transform-es2015-destructuring // 編譯解構賦值

  • transform-es2015-duplicate-keys // 編譯對象中重複的key,實際上是轉換成計算對象屬性

  • transform-es2015-for-of // 編譯for...of

  • transform-es2015-function-name // 將function.name語義應用於全部的function

  • transform-es2015-literals // 編譯整數(8進制/16進制)和unicode

  • transform-es2015-modules-commonjs // 將modules編譯成commonjs

  • transform-es2015-object-super // 編譯super

  • transform-es2015-parameters // 編譯參數,包括默認參數,不定參數和解構參數

  • transform-es2015-shorthand-properties // 編譯屬性縮寫

  • transform-es2015-spread // 編譯展開運算符

  • transform-es2015-sticky-regex // 正則添加sticky屬性

  • transform-es2015-template-literals // 編譯模版字符串

  • transform-es2015-typeof-symbol // 編譯Symbol類型

  • transform-es2015-unicode-regex // 正則添加unicode模式

  • transform-regenerator // 編譯generator函數

babel-preset-es2016

es2016(ES7)相關方法轉譯使用的插件,具體見文檔

  • transform-exponentiation-operator // 編譯冪運算符

babel-preset-es2017

es2017(ES8)相關方法轉譯使用的插件,具體見文檔

  • syntax-trailing-function-commas // function最後一個參數容許使用逗號

  • transform-async-to-generator // 把async函數轉化成generator函數

babel-preset-latest

latest是一個特殊的presets,包括了es2015,es2016,es2017的插件,不過已經廢棄,使用babel-preset-env代替,具體見文檔

stage-x(stage-0/1/2/3/4)

stage-x預設中的任何轉換都是還沒有被批准爲發佈Javascript的語言(如ES6 / ES2015)的更改。

stage-x和上面的es2015等有些相似,可是它是按照JavaScript的提案階段區分的,一共有5個階段。而數字越小,階段越靠後,存在依賴關係。也就是說stage-0是包括stage-1的,以此類推。

babel-preset-stage-4:

stage-4的插件:

babel-preset-stage-3:

除了stage-4的內容,還包括如下插件:

babel-preset-stage-2:

除了stage-3的內容,還包括如下插件:

babel-preset-stage-1:

除了stage-2的內容,還包括如下插件:

babel-preset-stage-0:

除了stage-1的內容,還包括如下插件:

爲了方便,咱們暫時引用 babel-preset-envbabel-preset-stage-2這兩個預設。爲了啓用預設,必須在.babelrc文件中定義預設的相關配置,這裏參考vue-cli 模板中的配置
安裝:

npminstall --save-dev babel-preset-env babel-preset-stage-2

.babelrc配置說明:

{
  "presets": [
    ["env", { 
      "modules": false 
    }],
    "stage-2"
  ]
}

Babel 插件(plugins)

咱們看一下預設的構成就知道,其實就是plugins的組合。若是你不採用presets,徹底能夠單獨引入某個功能,好比如下的設置就會引入編譯箭頭函數的功能,在.babelrc文件中進行配置:

{
  "plugins": ["transform-es2015-arrow-functions"]
}

babel-polyfill 與 babel-runtime

Babel默認只轉換新的JavaScript句法(syntax),而不轉換新的API,好比Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(好比 Object.assign)都不會轉碼。

舉例來講,ES6在 Array 對象上新增了 Array.from 方法。Babel就不會轉碼這個方法。若是想讓這個方法運行,必須使用 babel-polyfill ,爲當前環境提供一個墊片。babel-polyfill 是對瀏覽器缺失API的支持。

babel-runtime 是爲了減小重複代碼而生的。 babel生成的代碼,可能會用到一些_extend(), classCallCheck() 之類的工具函數,默認狀況下,這些工具函數的代碼會包含在編譯後的文件中。若是存在多個文件,那每一個文件都有可能含有一份重複的代碼。babel-runtime插件可以將這些工具函數的代碼轉換成require語句,指向爲對babel-runtime的引用,如require('babel-runtime/helpers/classCallCheck'). 這樣, classCallCheck的代碼就不須要在每一個文件中都存在了。

啓用插件 babel-plugin-transform-runtime 後,Babel 就會使用 babel-runtime 下的工具函數。除此以外,babel 還爲源代碼的非實例方法(Object.assign,實例方法是相似這樣的 "foobar".includes("foo"))和 babel-runtime/helps 下的工具函數自動引用了 polyfill。這樣能夠避免污染全局命名空間,很是適合於 JavaScript 庫和工具包的實現。

總結:

  • 具體項目仍是須要使用 babel-polyfill,只使用 babel-runtime 的話,實例方法不能正常工做(例如 "foobar".includes("foo"));

  • JavaScript 庫和工具可使用 babel-runtime,在實際項目中使用這些庫和工具,須要該項目自己提供 polyfill。

  • transform-runtime只會對es6的語法進行轉換,而不會對新api進行轉換。若是須要轉換新api,就要引入babel-polyfill。

安裝插件

npm install --save-dev babel-plugin-transform-runtime

.babelrc 配置:

{
  "plugins": ["transform-runtime", options]
}

options主要有如下設置項:

  • helpers: boolean,默認true,使用babel的helper函數;

  • polyfill: boolean,默認true,使用babel的polyfill,可是不能徹底取代babel-polyfill;

  • regenerator: boolean,默認true,使用babel的regenerator;

  • moduleName: string,默認babel-runtime,使用對應module處理。

注:默認moduleName爲babel-runtime,這裏咱們能夠沒必要顯式的下載babel-runtime,由於babel-plugin-transform-runtime依賴於babel-runtime。

babel-register

babel-register 模塊改寫 require 命令,爲它加上一個鉤子。此後,每當使用 require 加載 .js 、 .jsx 、 .es 和 .es6 後綴名的文件,就會先用Babel進行轉碼。引入babel-register,這樣後面的文件就能夠用 import 代替require,import的優勢在於能夠引入所需方法或者變量,而不須要加載整個模塊,提升了性能。

安裝:

npm install --save-dev babel-register

這部分咱們又介紹了下面幾個模塊的安裝:

babel-loader
babel-core
babel-preset-env 
babel-preset-stage-2 
babel-plugin-transform-runtime
babel-register

webpack 中如何使用 vue?

既然本文的目標是vue的自定義模板工程,那麼天然這裏須要單獨介紹一下webpack中vue相關的插件。

Vue2文件比較

npm 安裝:

npm install --save vue

vue2 通過 2.2 版本升級後, 文件變成 8 個:

UMD CommonJS ES Module
獨立構建 vue.js vue.common.js vue.esm.js
運行構建 vue.runtime.js vue.runtime.common.js vue.runtime.esm.js

vue.min.js 和 vue.runtime.min.js 都是對應的壓縮版。

  • AMD:異步模塊規範

  1. 沒有單獨提供 AMD 模塊的版本,可是UMD版本中進行了包裝,能夠直接用做 AMD 模塊,使用方法以下:

define(["Vue"],function(Vue) {
    function myFn() {
        ...
    }
    return myFn;
});
  • CommonJS:
    node中經常使用的模塊規範,經過require引入模塊,module.exports導出模塊。

...
function Vue$3() {
   ...
}
...
module.exports = Vue$3;
  • UMD: 通用模塊規範
    兼容了AMD和CommonJS,同時還支持老式的「全局」變量規範:

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.Vue = factory());
}(this, (function () { 'use strict';
    ...
    function Vue$3() {
        ...
    }
    ...
    return Vue$3;
})));
  • ES Module
    ES6在語言標準的層面上,實現的模塊功能。模塊功能主要由兩個命令構成:export和import。export命令用於規定模塊的對外接口,import命令用於輸入其餘模塊提供的功能。

...
function Vue$3() {
   ...
}
export default Vue$3;

總結:

  • vue.js 和 vue.runtime.js 能夠用於直接 CDN 引用;

  • vue.common.js和vue.runtime.common.js可使用Webpack1 / Browserify 打包構建;

  • vue.esm.js和vue.runtime.esm.js可使用Webpack2 / rollup 打包構建。

vue有兩種構建方式,獨立構建和運行時構建。它們的區別獨立構建前者包含模板編譯器而運行構建不包含。模板編譯器的職責是將模板字符串編譯爲純 JavaScript 的渲染函數。若是你想要在組件中使用 template 選項,你就須要編譯器。

  • 獨立構建包含模板編譯器並支持 template 選項。 它也依賴於瀏覽器的接口的存在,因此你不能使用它來爲服務器端渲染。

  • 運行時構建不包含模板編譯器,所以不支持 template 選項,只能用 render 選項,但即便使用運行時構建,在單文件組件中也依然能夠寫模板,由於單文件組件的模板會在構建時預編譯爲 render 函數。運行時構建比獨立構建要輕量30%,只有 17.14 Kb min+gzip大小。

獨立構建方式能夠這樣使用template選項:

import Vue from 'vue'
new Vue({
  template: `
    <div id="app">
      <h1>Basic</h1>
    </div>
  `
}).$mount('#app')

這裏咱們使用ES Module規範,默認 NPM 包導出的是運行時構建。爲了使用獨立構建,在 webpack 配置中添加下面的別名:

resolve: {
  alias: {
    'vue$': 'vue/dist/vue.esm.js'
  }
}

vue-loader

安裝:

npm install --save-dev vue-loader vue-template-compiler

vue-loader 依賴於 vue-template-compiler。

vue-loader 是一個 Webpack 的 loader,能夠將用下面這個格式編寫的 Vue 組件轉換爲 JavaScript 模塊。這裏有一些 vue-loader 提供的很酷的特性:

  • ES2015 默認支持;

  • 容許對 Vue 組件的組成部分使用其它 Webpack loaders,好比對 <style> 使用 SASS 和對 <template> 使用 Jade;

  • .vue 文件中容許自定義節點,而後使用自定義的 loader 處理他們;

  • <style> <template> 中的靜態資源看成模塊來對待,並使用 Webpack loaders 進行處理;

  • 對每一個組件模擬出 CSS 做用域;

  • 支持開發期組件的熱重載。

簡而言之,編寫 Vue.js 應用程序時,組合使用 Webpack 和 vue-loader 能帶來一個現代,靈活而且很是強大的前端工做流程。

在 Webpack 中,全部的預處理器須要匹配對應的 loader。 vue-loader 容許你使用其它 Webpack loaders 處理 Vue 組件的某一部分。它會根據 lang 屬性自動推斷出要使用的 loaders。

上述咱們提到extract-text-webpack-plugin插件提取css,這裏說明一下.vue中style標籤之間的樣式提取的辦法:

var ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            css: ExtractTextPlugin.extract({
              use: 'css-loader',
              fallback: 'vue-style-loader' // <- 這是vue-loader的依賴,因此若是使用npm3,則不須要顯式安裝
            })
          }
        }
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("app.css")
  ]
}

pug 模板

用過模板的都知道,熟悉了模板寫起來快多了,大名鼎鼎的jade恐怕無人不知吧。pug是什麼鬼?第一次聽到的時候我也好奇了,而後查了一下才知道,Pug原名不叫Pug,原來是大名鼎鼎的jade,後來因爲商標的緣由,改成Pug,哈巴狗。如下是官方解釋:

it has been revealed to us that "Jade" is a registered trademark, and as a result a rename is needed. After some discussion among the maintainers, "Pug" has been chosen as the new name for this project.

簡單看了看仍是原來jade熟悉的語法規則,果斷在這個模板工程裏面用上。

vue-loader裏面對於模版的處理方式略有不一樣,由於大多數 Webpack 模版處理器(好比 pug-loader)會返回模版處理函數,而不是編譯的 HTML 字符串,咱們使用原始的 pug 替代 pug-loader:

npm install pug --save-dev

使用:

<template lang="pug">
div
  h1 Hello world!
</template>

重要: 若是你使用 vue-loader@<8.2.0, 你還須要安裝 template-html-loader

PostCSS

安裝vue-loader的時候默認安裝了postcss,由vue-loader處理的 CSS 輸出,都是經過PostCSS進行做用域重寫,你還能夠爲 PostCSS 添加自定義插件,例如autoprefixer或者CSSNext

在 webpack 工程中使用 postcss,咱們須要下載 postcss-loader:

npm install --save-dev postcss-loader

cssnext

cssnext 是一個 CSS transpiler,容許你使用最新的 CSS 語法。cssnext 把 新 CSS 規範轉換成兼容性更強的 CSS,因此不須要等待各類瀏覽器支持。

安裝:

npm install --save-dev postcss-cssnext

postcss.config.js:

module.exports = {
    plugins: [
        require('postcss-cssnext')
    ]
}

webpack.config.js:

module.exports = {
    module: {
        loaders: [
            {
                test:   /\.css$/,
                use: ['style-loader', 'css-loader', 'postcss-loader']
            }
        ]
    }
}

cssnext 依賴了autoprefixer,因此咱們無需顯式下載autoprefixer。更多關於postcss的插件能夠看這裏:postcss plugins

這一部分咱們學習了這些依賴:

vue
vue-loader 
vue-template-compiler
pug
postcss-loader
postcss-cssnext

webpack2 開啓 eslint 校驗

規範本身的代碼從ESlint開始。ESlint和webpack集成,在babel編譯代碼開始前,進行代碼規範檢測。這裏咱們使用javascript-style-standard風格的校驗。

主要依賴的幾個包:

eslint —— 基礎包
eslint-loader —— webpack loader
babel-eslint —— 校驗babel
eslint-plugin-html —— 提取並檢驗你的 .vue 文件中的 JavaScript
eslint-friendly-formatter —— 生成美化的報告格式

# javascript-style-standard 依賴的包
eslint-config-standard
eslint-plugin-import
eslint-plugin-node
eslint-plugin-promise
eslint-plugin-standard

安裝:

npm install --save-dev eslint eslint-loader babel-eslint eslint-plugin-html eslint-friendly-formatter eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-node eslint-plugin-promise eslint-plugin-standard

關於eslint的配置方式,比較多元化,具體能夠看配置文檔

  • js註釋

  • .eslintrc.*文件

  • package.json裏面配置eslintConfig字段

安裝eslint-loader以後,咱們能夠在webpack配置中使用eslint加載器。webpack.config.js

...
module: {
  loaders: [
    {
         test: /\.vue|js$/,
         enforce: 'pre',
         include: path.resolve(__dirname, 'src'),
         exclude: /node_modules/,
         use: [{
             loader: 'eslint-loader',
             options: {
                 formatter: require('eslint-friendly-formatter')
             }
         }]
    }
  ]
},
...

此外,咱們既能夠在webpack配置文件中指定檢測規則,也能夠遵循最佳實踐在一個專門的文件中指定檢測規則,咱們就採用後面的方式。
在根目錄下:

touch .eslintrc.js

.eslintrc.js:

module.exports = {
  root: true,
  parser: 'babel-eslint',
  parserOptions: {
    sourceType: 'module'
  },
  env: {
    browser: true
  },
  extends: 'standard',
  // required to lint *.vue files
  plugins: [
    'html'
  ],
  // add your custom rules here
  rules: {
    // allow paren-less arrow functions
    'arrow-parens': 0,
    // allow async-await
    'generator-star-spacing': 0,
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
  }
}

這部份咱們主要學習了一下eslint相關插件的含義和配置方法。

建立屬於你的模板

若是你對官方的模板不感興趣,你能夠本身fork下來而後進行修改(或者從新寫一個),而後用 vue-cli 來調用。由於 vue-cli 能夠直接拉取 git源:

vue init username/repo my-project

這裏咱們參考vue-cli的模板工程本身寫一個模板工程,主要是須要經過meta.*(js,json)進行配置:

module.exports = {
  "helpers": {
    "if_or": function (v1, v2, options) {
      if (v1 || v2) {
        return options.fn(this);
      }

      return options.inverse(this);
    }
  },
  "prompts": {
    "name": {
      "type": "string",
      "required": true,
      "message": "Project name"
    },
    "version": {
      "type": "string",
      "required": false,
      "message": "Project version",
      "default": "1.0.0"
    },
    "description": {
      "type": "string",
      "required": false,
      "message": "Project description",
      "default": "A Vue.js project"
    },
    "author": {
      "type": "string",
      "message": "Author"
    },
    "router": {
      "type": "confirm",
      "message": "Install vue-router?"
    },
    "vuex": {
      "type": "confirm",
      "message": "Install vuex?"
    }
  },
  "completeMessage": "To get started:\n\n  {{^inPlace}}cd {{destDirName}}\n  {{/inPlace}}npm install\n  npm run dev\n\nDocumentation can be found at https://github.com/zhaomenghuan/vue-webpack-template"
};

這裏咱們就是採用最簡單的方式,對於vue-router、vuex的配置每一個人習慣不同,因此不寫在模板工程裏面。

而後使用vue-cli使用這個模板建立工程,沒有安裝vue-cli的執行:

npm install --global vue-cli

而後建立工程:

# 建立一個基於 webpack 模板的新項目
vue init zhaomenghuan/vue-webpack-template my-project
# 安裝依賴,走你
cd my-project
npm install
npm run dev

這裏按照國際慣例安利一下本文的模板工程:vue-webpack-template

參考

webpack官方文檔
babel官方文檔
vue-loader中文文檔
JavaScript books by Dr. Axel Rauschmayer
ES7新特性及ECMAScript標準的制定流程
如何寫好.babelrc?Babel的presets和plugins配置解析
babel的polyfill和runtime的區別
webpack2集成eslint

clipboard.png


近期在segmentfault講堂開設了一場關於html5+ App開發工程化實踐之路的講座,歡迎前來圍觀:https://segmentfault.com/l/15...

相關文章
相關標籤/搜索