入職第三天:vue-loader在專案中是如何配置的

碼農土哥發表於2018-06-02

什麼是vue-loader

這是我入職第三天的故事,在寫這篇文章之前,先來看看我們們今天要講的主角——vue-loader,你對它瞭解多少?

入職第三天:vue-loader在專案中是如何配置的

這是我今天的回答,確實,vue-loader是webpack的一個loader,用於處理.vue檔案。 .vue 檔案是一個自定義的檔案型別,用類 HTML 語法描述一個 Vue 元件。每個 .vue 檔案包含三種型別的頂級語言塊 <template><script><style>

vue-loader 會解析檔案,提取每個語言塊,如有必要會通過其它 loader 處理(比如<script>預設用babel-loader處理,<style>預設用style-loader處理),最後將他們組裝成一個 CommonJS 模組,module.exports 出一個 Vue.js 元件物件。

vue-loader 支援使用非預設語言,比如 CSS 前處理器,預編譯的 HTML 模版語言,通過設定語言塊的 lang 屬性。例如,你可以像下面這樣使用 Sass 語法編寫樣式:

<style lang="sass">
  /* write Sass! */
</style>
複製程式碼

知道了什麼是vue-loader之後,我們先來建立專案。為了快速地聊聊vue-loader,我在這裡推薦用腳手架工具 @vue/cli 來建立一個使用 vue-loader 的專案:

npm install -g @vue/cli
vue create hello-world
cd hello-world
npm run serve
複製程式碼

這個過程我可以等等你們,because this might take a while...

入職第三天:vue-loader在專案中是如何配置的

當你在瀏覽器裡輸入localhost:8080之後,瀏覽器會友善地渲染出一個Welcome to Your Vue.js App的歡迎頁面。

緊接著,我們需要開啟你擅長的編輯器,這裡我選用的是VSCode,順手將專案匯入進來,你會看到最原始的一個專案工程目錄,裡面只有一些簡單的專案構成,還沒有vue-loader的配置檔案:

入職第三天:vue-loader在專案中是如何配置的

首先,我們需要在專案根目錄下面新建一個webpack.config.js檔案,然後開始我們今天的主題。

手動配置css到單獨檔案

說到提取css檔案,我們應該先去terminal終端去安裝一下相關的npm包:

npm install extract-text-webpack-plugin --save-dev
複製程式碼

先來說個簡答的方法,上程式碼:

// webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin")

module.exports = {
  // other options...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          extractCSS: true
        }
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("style.css")
  ]
}
複製程式碼

上述內容將自動處理 *.vue 檔案內的 <style> 提取到style.css檔案裡面,並與大多數前處理器一樣開箱即用。

注意這只是提取 *.vue 檔案 - 但在 JavaScript 中匯入的 CSS 仍然需要單獨配置。

接下來我們再看看如何手動配置:

// webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin")

module.exports = {
  // other options...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            css: ExtractTextPlugin.extract({
              use: 'css-loader',
              fallback: 'vue-style-loader' // 這是vue-loader的依賴
            })
          }
        }
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("style.css")
  ]
}
複製程式碼

此舉便將所有 Vue 元件中的所有已處理的 CSS 提取到了單個的 CSS 檔案。

如何構建生產環境

生產環境打包,目的只有兩個:1.壓縮應用程式碼;2.去除Vue.js中的警告

下面的配置僅供參考:

// webpack.config.js
module.exports = {
  // ... other options
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin()
  ]
}
複製程式碼

當然,如果我們不想在開發過程中使用這些配置,有兩種方法可以解決這個問題:

1.使用環境變數動態構建;

例如:const isDev = process.env.NODE_ENV==='development'

或者:const isProd = process.env.NODE_ENV === 'production'

2.使用兩個分開的 webpack 配置檔案,一個用於開發環境,一個用於生產環境。把可能共用的配置放到第三個檔案中。

借鑑大牛的經驗

這裡提供一個網上標準的寫法,名字叫做vue-hackernews-2.0,這裡是傳送門:https://github.com/vuejs/vue-hackernews-2.0。

其中共用的配置檔案webpack.base.config.js的程式碼如下:

const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')

const isProd = process.env.NODE_ENV === 'production'

module.exports = {
  devtool: isProd
    ? false
    : '#cheap-module-source-map',
  output: {
    path: path.resolve(__dirname, '../dist'),
    publicPath: '/dist/',
    filename: '[name].[chunkhash].js'
  },
  resolve: {
    alias: {
      'public': path.resolve(__dirname, '../public')
    }
  },
  module: {
    noParse: /es6-promise\.js$/, // avoid webpack shimming process
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          compilerOptions: {
            preserveWhitespace: false
          }
        }
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[ext]?[hash]'
        }
      },
      {
        test: /\.styl(us)?$/,
        use: isProd
          ? ExtractTextPlugin.extract({
              use: [
                {
                  loader: 'css-loader',
                  options: { minimize: true }
                },
                'stylus-loader'
              ],
              fallback: 'vue-style-loader'
            })
          : ['vue-style-loader', 'css-loader', 'stylus-loader']
      },
    ]
  },
  performance: {
    maxEntrypointSize: 300000,
    hints: isProd ? 'warning' : false
  },
  plugins: isProd
    ? [
        new VueLoaderPlugin(),
        new webpack.optimize.UglifyJsPlugin({
          compress: { warnings: false }
        }),
        new webpack.optimize.ModuleConcatenationPlugin(),
        new ExtractTextPlugin({
          filename: 'common.[chunkhash].css'
        })
      ]
    : [
        new VueLoaderPlugin(),
        new FriendlyErrorsPlugin()
      ]
}

複製程式碼

然後看看用於開發環境的webpack.client.config.js的配置是如何寫的:

const webpack = require('webpack')
const merge = require('webpack-merge')
const base = require('./webpack.base.config')
const SWPrecachePlugin = require('sw-precache-webpack-plugin')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')

const config = merge(base, {
  entry: {
    app: './src/entry-client.js'
  },
  resolve: {
    alias: {
      'create-api': './create-api-client.js'
    }
  },
  plugins: [
    // strip dev-only code in Vue source
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
      'process.env.VUE_ENV': '"client"'
    }),
    // extract vendor chunks for better caching
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: function (module) {
        // a module is extracted into the vendor chunk if...
        return (
          // it's inside node_modules
          /node_modules/.test(module.context) &&
          // and not a CSS file (due to extract-text-webpack-plugin limitation)
          !/\.css$/.test(module.request)
        )
      }
    }),
    // extract webpack runtime & manifest to avoid vendor chunk hash changing
    // on every build.
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest'
    }),
    new VueSSRClientPlugin()
  ]
})

if (process.env.NODE_ENV === 'production') {
  config.plugins.push(
    // auto generate service worker
    new SWPrecachePlugin({
      cacheId: 'vue-hn',
      filename: 'service-worker.js',
      minify: true,
      dontCacheBustUrlsMatching: /./,
      staticFileGlobsIgnorePatterns: [/\.map$/, /\.json$/],
      runtimeCaching: [
        {
          urlPattern: '/',
          handler: 'networkFirst'
        },
        {
          urlPattern: /\/(top|new|show|ask|jobs)/,
          handler: 'networkFirst'
        },
        {
          urlPattern: '/item/:id',
          handler: 'networkFirst'
        },
        {
          urlPattern: '/user/:id',
          handler: 'networkFirst'
        }
      ]
    })
  )
}

module.exports = config

複製程式碼

最後來看看開發環境中的webpack.server.config.js的配置是怎麼寫的:

const webpack = require('webpack')
const merge = require('webpack-merge')
const base = require('./webpack.base.config')
const nodeExternals = require('webpack-node-externals')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

module.exports = merge(base, {
  target: 'node',
  devtool: '#source-map',
  entry: './src/entry-server.js',
  output: {
    filename: 'server-bundle.js',
    libraryTarget: 'commonjs2'
  },
  resolve: {
    alias: {
      'create-api': './create-api-server.js'
    }
  },
  // https://webpack.js.org/configuration/externals/#externals
  // https://github.com/liady/webpack-node-externals
  externals: nodeExternals({
    // do not externalize CSS files in case we need to import it from a dep
    whitelist: /\.css$/
  }),
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
      'process.env.VUE_ENV': '"server"'
    }),
    new VueSSRServerPlugin()
  ]
})

複製程式碼

如何進行程式碼檢驗

你可能有疑問,在 .vue 檔案中你怎麼檢驗你的程式碼,因為它不是 JavaScript。我們假設你使用 ESLint (如果你沒有使用話,你應該去使用!)。

首先你要去安裝eslint-loader:

npm install eslint eslint-loader --save-dev
複製程式碼

然後將它應用在pre-loader上:

// webpack.config.js
module.exports = {
  // ... other options
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        exclude: /node_modules/
      }
    ]
  }
}
複製程式碼

這樣你的 .vue 檔案會在開發期間每次儲存時自動檢驗。

關於更多eslint的介紹,你可以翻看我之前寫的文章《我是如何在公司專案中使用ESLint來提升程式碼質量的》,這篇文章裡面有更多的應用小技巧。

文章預告:下一篇文章我將淺談下css-modules的配置以及.editorconfig。最新文章都會第一時間更新在我的公眾號<閏土大叔>裡面,歡迎關注~

入職第三天:vue-loader在專案中是如何配置的

相關文章