1. 程式人生 > >webpack + vue專案搭建

webpack + vue專案搭建

轉自:[造輪子教程]十分鐘搭建Webpack+Vue專案

原文使用的版本比較老,且看且珍惜。

之前說到的vue.js的安裝–vue-cli腳手架採用的vue-cli腳手架進行安裝的自動構建的專案,但是實際專案中可能會有特殊的要求,所以沒有使用到腳手架

推薦vue專案目錄結構:

|-- ./config  // 全域性變數
|-- ./dist    // 編譯後的專案程式碼
|-- ./src     // 專案原始碼
    |-- apis        // api封裝
    |-- components  // Vue元件
    |-- lib         // js工具類
    |
-- router // 路由 |-- store // Vuex的store |-- modules // Vuex模組 |-- style // css |-- views // 頁面元件 |-- index.js // 入口檔案 |-- ./webpack.config // Webpack各種環境的配置檔案 |-- ./package.json

首先安裝Node.js,選擇需要的版本自行安裝,這裡不多贅述。

本文原始碼在這:webpack + vue專案搭建,只要有安裝Node.js,下載後執行npm install

,後執行npm run dev即可。

初始化專案

為了方便閱讀,這裡的專案目錄結構使用的是上面的推薦目錄。

在專案的根目錄處(此處為./)使用使用命令列執行npm init -y-y的作用是跳過資訊填寫)建立package.json

入口檔案

1、在根目錄下直接建立一個index.html,作為頁面的入口檔案。

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible"
content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <title>myProject</title> </head> <body> <div id="app">{{ message }}</div> <!-- Vue模板入口 --> <script src="dist/index.js" charset="utf-8"></script> </body> </html>

2、在src下建立一個index.js,作為Vue的入口檔案

// import...from的語法是ES6的,需要用到babel,後面再說
// require的語法是Commonjs的,webpack已經實現了,可以直接使用
const Vue = require('vue')
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!'
  }
})

3、安裝模組

安裝Vue:npm install --save vue

安裝Webpack:npm install --save-dev webpack

4、使用webpack編譯打包

除非在全域性安裝webpack,使用本地安裝的webpack你需要寫一長串的命令.\node_modules\.bin\webpack src\index.js dist\index.js,所以建議在package.jsonscript加入執行指令碼,新增之後package.json如下:

{
  "name": "myProject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack src/index.js dist/index.js"  // <---新增這句
  },
  "keywords": [],
  "author": "Headmaster_Tan",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.4.2"
  },
  "devDependencies": {
    "webpack": "^3.5.5"
  }
}

然後你可以執行npm run dev命令進行打包,此時開啟index.html,如無意外應該是會報錯的。這是一個[email protected] -> [email protected]的版本升級問題,報錯內容如下:

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

解決辦法是使用下面的webpack配置檔案對'vue'進行定義,報錯原因詳情可以看這裡:Vue 2.0 升(cai)級(keng)之旅

編寫webpack配置檔案

上一步中直接使用webpack執行指令碼webpack [入口檔案] [出口檔案],顯然對於後期新增webpack外掛和不同環境的配置是不行的。

1、在專案根目錄下建立webpack.config資料夾專門用於存放webpack的配置檔案(當然也是完全可以只建一個webpack.config.js檔案的)

2、為了讓配置檔案不同的編譯環境能夠複用(例如loaders的配置,不管在開發環境還是生產環境肯定都是一樣的),在webpack.config中首先建立一個base.js

const path = require('path')
const root = path.resolve(__dirname, '..')  // 專案的根目錄絕對路徑

module.exports = {
  entry: path.join(root, 'src/index.js'),    // 入口檔案路徑
  output: {
    path: path.join(root, 'dist'),    // 出口目錄
    filename: 'index.js'   // 出口檔名
  }
}

上面這段配置就實現了webpack src/index.js dist/index.js的功能,還可以額外拓展一下,變成:

const path = require('path')
const root = path.resolve(__dirname, '..')    // 專案的根目錄絕對路徑

module.exports = {
  entry: path.join(root, 'src/main.js'),    // 入口檔案路徑
  output: {
    path: path.join(root, 'dist'),    // 出口目錄
    filename: 'main.js'   // 出口檔名
  },
  resolve: {
    alias: {    // 配置目錄別名
      // 在任意目錄下require('components/example') 相當於require('專案根目錄/src/components/example')
      components: path.join(root, 'src/components'),
      views: path.join(root, 'src/views'),
      styles: path.join(root, 'src/styles'),
      store: path.join(root, 'src/store'),
      'vue': 'vue/dist/vue.js'    // 這裡就是解決上面[email protected] -> [email protected]版本升級的報錯問題
    },
    // 這裡注意在新版本的webpack中,extensions是不能包含''空串的
    extensions: ['.js', '.vue'],    // 引用js和vue檔案可以省略字尾名
  },
  module: {   // 配置loader
    // 在這裡 -loader 不能忽略,乖乖加上吧
    loaders: [
      {test: /\.vue$/, loader: 'vue-loader'},    // 所有.vue結尾的檔案,使用vue-loader
      {test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/}   // .js檔案使用babel-loader,切記排除node_modules目錄
    ]
  }
}

babel-loader就是將ES6的語法解析為ES5,要用到babel則根目錄下要新建.babelrc檔案用於配置babel

{
  "presets": ["es2015"]
}

使用了vue-loaderbabel-loader需要安裝包:

npm install --save-dev vue-loader babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015 css-loader vue-style-loader vue-hot-reload-api vue-html-loader

3、在webpack.config資料夾中建立dev.js檔案:

const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseConfig = require('./base')
const root = path.resolve(__dirname, '..')

module.exports = merge(baseConfig, {})

上面程式碼僅僅是匯出了跟base.js一模一樣的配置,下面我們新增更多用於dev(開發環境)的配置。

webpack-merge 用於合併兩個配置檔案,需要安裝

npm install --save-dev webpack-merge

4、使用webpack-dev-server,開啟一個小型伺服器,不需要再手動開啟index.html進行除錯了,修改配置檔案為:

module.exports = merge(baseConfig, {
  devServer: {
    historyApiFallback: true, // 404的頁面會自動跳轉到/頁面
    inline: true, // 檔案改變自動重新整理頁面
    port: 8080, // 伺服器埠
  },
  devtool: 'source-map' // 用於標記編譯後的檔案與編譯前的檔案對應位置,便於除錯
})

5、新增熱替換配置,每次改動檔案不會再整個頁面都重新整理,並使用HtmlWebpackPlugin,實現js入口檔案自動注入

安裝webpack-dev-server: npm install --save-dev webpack-dev-server

// ...同上
const HtmlWebpackPlugin = require('html-webpack-plugin')  // 實現js入口檔案自動注入
module.exports = merge(baseConfig, {
  entry: [
    'webpack/hot/dev-server', // 熱替換處理入口檔案
    path.join(root, 'src/index.js')
  ],
  devServer: { /* 同上 */ },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),   // 新增熱替換外掛
    new HtmlWebpackPlugin({
      template: path.join(root, 'index.html'),  // 模板檔案
      inject: 'body' // js的script注入到body底部
    })
  ]
})

這裡的HotModuleReplacementPlugin是webpack內建的外掛,不需要安裝

但HtmlWebpackPlugin需要自行安裝:

npm install --save-dev html-webpack-plugin

在檔案頭中引入

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

修改index.html,去掉入口檔案的javascript引入:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>myProject</title>
  </head>
  <body>
    <div id="app">{{ message }}</div>
  </body>
</html>

6、最後修改package.json中的webpack執行指令碼為:

{
  "dev": "webpack-dev-server --config webpack.config/dev.js"
}

為了測試webpack配置是否都生效了,下面建立一個vue元件src/components/Hello.vue:

<template>
  <div>{{ message }}</div>
</template>

<script>
  export default {
    data: () => ({ message: 'Hello Vue.js!' })
  }
</script>

既然用到了vue元件,那自然需要安裝vue-template-compiler: npm install --save-dev vue-template-compiler

修改index.js

import Vue  from 'vue'
import Hello from './components/Hello.vue'

new Vue({
  el: '#app',
  template: '<div><hello></hello></div>',
  components: { Hello }
})

執行npm run dev,開啟瀏覽器localhost:8080,檢視結果:

Hello Vue.js!

如果這裡出現了報錯,而且長這個樣子的:

Error: Cannot find module 'webpack/bin/config-yargs'
    at Function.Module._resolveFilename (module.js:485:15)
    at Function.Module._load (module.js:437:25)
    at Module.require (module.js:513:17)
    ······balabala

那是因為webpackwebpack-dev-server的版本不相容導致的,檢視下版本重新下載就好了。

配置路由

1、安裝vue-router: npm install --save vue-router

2、在src目錄下建立views(用於存放頁面元件)和router(用於存放所有路由相關的配置)資料夾。

3、新增頁面元件src/views/Home.vue

<template>
  <div><hello></hello></div>
</template>

<script>
  import Hello from 'components/Hello'
  export default {
    components: { Hello }
  }
</script>

下面的幾個檔案因為是使用的新版環境,所以和原文的會有很大出入。

新增專案路由配置檔案src/router/routes.js

import Home from 'views/Home'

export default [{
  path: '/',        // 代表訪問 '/' 的時候其實是訪問Home.vue頁面
  name: 'home',
  component: Home
}]

新增路由入口檔案src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'

Vue.use(Router)

const router = new Router({
  hashbang: false, // 關閉hash模式
  history: true, // 開啟html5 history模式
  linkActiveClass: 'active', // v-link啟用時新增的class,預設是`v-link-active`
  routes
})

// 全域性導航鉤子
router.beforeEach((to, from, next) => {
  console.log('---------> ' + to.path)  // 每次調整路由時列印,便於除錯
  next()
})

export default router

修改src/index.js

import Vue  from 'vue'
import router from './router'

const App = new Vue({
  router
}).$mount('#app')

最後別忘了index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>myProject</title>
</head>
<body>
  <div id="app">
    <router-view></router-view>    <!--路由替換位置-->
  </div>
</body>
</html>

重新執行npm run dev,開啟瀏覽器localhost:8080(訪問Home.vue頁面)檢視效果

Hello Vue.js

配置Vuex

vuex通常用於存放和管理不同元件中的共用狀態,例如不同路由頁面之間的公共資料

vuex中的幾個概念:

  • state: 狀態,即資料
  • store: 資料的集合,一個vuex引用,僅有一個store,包含n個state
  • getters:state不能直接取值,使用getters返回需要的state
  • mutations: state不能直接賦值,通過mutation定義最基本的操作
  • actions: 在action中呼叫一個或多個mutation
  • modules: store和state之間的一層,便於大型專案管理,store包含多個module,module包含state、mutation、action

本教程將以一個全域性計數器作為例子

1、安裝vuex: npm install --save vuex

2、新增src/store資料夾,存放vuex相關檔案,新增src/store/modules用於vuex分模組管理。

新增src/store/types.js,vuex的所有mutation type都放在一起,不建議分開多個檔案,有效避免重名情況:

export const INCREASE = 'INCREASE' // 累加
export const RESET = 'RESET' // 清零

3、編寫vuex模組,新增counter.js模組目錄store/modules/counter.js

import * as types from 'store/types'
// state
const state = {
  count: 0
}
// getters
const getters = {
  getCount: state => state.count
}
// actions
const actions = {
  increase({ commit }) {
    commit(types.INCREASE)  // 呼叫type為INCREASE的mutation
  },
  reset({ commit }) {
    commit(types.RESET)     // 呼叫type為RESET的mutation
  }
}
// mutations
const mutations = {
  [types.INCREASE](state) {
    state.count++
  },
  [types.RESET](state) {
    state.count = 0
  }
}

export default {
  state,
  getters,
  actions,
  mutations
}

4、新增store/index.js,作為vuex入口檔案(共用的getters和actions可拆分到store/getters.jsstore/actions.js,然後在store/index.js進行匯入,這裡因為沒有,所以就直接寫了):

import Vue from 'vue'
import Vuex from 'vuex'
import counter from './modules/counter'

Vue.use(Vuex) // 確保在new Vuex.Store()之前

export default new Vuex.Store({
  getters: {},
  actions: {},
  modules: {
    counter
  }
})

5、修改src/index.js,將store引入並新增到App中:

import Vue from 'vue'
import router from './router'
import store from 'store'

const app = new Vue({
  router,
  store
}).$mount('#app')

6、最後改造一下components/Hello.vue,可以看到vuex v2變化了蠻多:

<template>
<div>
  <p>{{ message }}</p>
  <p>click count: {{ getCount }}</p>
  <button @click="increase">increase</button>
  <button @click="reset">reset</button>
</div>
</template>

<script>
// vuex 提供了獨立的構建工具函式 Vuex.mapGetters, Vuex.mapActions
import { mapGetters, mapActions } from 'vuex'

export default {
  data: () => {
    return {
      message: 'Hello Vue.js!'
    }
  },
  computed: mapGetters({
    getCount: 'getCount'
  }),
  methods: mapActions([
    'increase',
    'reset'
  ])
}
</script>

重新執行npm run dev,開啟瀏覽器localhost:8080,檢視效果。

配置eslint

這不是必須的,不想要可以跳過。

雖然eslint不是必須的,但是強烈建議用在所有的JavaScript專案中。

對於個人開發,可以在程式設計過程中發現並提示語法錯誤,有效過濾各種低階錯誤

對於團隊開發,強制採用一致的編碼風格,保證專案的一致性,有效避免各種任性行為

但是一定要注意,eslint定義的只是編碼風格,規則是死的,人是活的,學會利用自定義規則的功能,增減規則

同時要知道,eslint檢測不通過不一定就是不能執行的,可能只是這種寫法違背了編碼風格,學會檢視控制的查詢具體錯誤原因

想要更好的eslint體驗,請根據不同編輯器安裝對應的eslint外掛,主流的編輯器已有相應的外掛

1、選擇合適的編碼風格

eslint提供了很多rules,可以直接在.eslintrc檔案的rules中一個一個地配置

顯然我們大多數情況下不需要這麼做,晚上已經有一些比較多人使用的風格了,本文推薦使用standard,點開這個連結,可以看到支援的編輯器和對應的外掛名,自行去安裝

2、配置.eslintrc檔案,在根目錄下建立.eslintrc檔案:

{
  "parser": "babel-eslint", // 支援babel
  "extends": "standard", // 使用eslint-config-standard的配置
  "plugins": [
    "html" // 支援.vue檔案的檢測
  ],
  "env": {
    "browser": true, // 不會將window上的全域性變數判斷為未定義的變數
    "es6": true // 支援es6的語法
  },
  "rules": { // 自定義個別規則寫在這,0忽略,1警告,2報錯
    "no-unused-vars": 1 // 將”未使用的變數“調整為警告級別,原為錯誤級別,更多規則請看官網
  }
}

結合不同編輯器的外掛,開啟js和vue檔案中,就可以看到提示了

根據使用的不同風格,安裝所需的包,本文安裝:

npm install --save-dev eslint babel-eslint eslint-config-standard eslint-plugin-standard eslint-plugin-html eslint-plugin-promise

webpack生產環境配置

前面已經配置過了開發環境下使用的配置檔案dev.js,對於生產環境,通常需要對編譯出來的檔案戶必須不過壓縮處理,提取公共模組等,就需要專門提供一個配置檔案。

1、新增webpack.config/pro.js檔案,把生產環境用不到的刪除,比如webpack-dev-server, webpack-hot-replacement

const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const baseConfig = require('./base')
const root = path.resolve(__dirname, '..')

module.exports = merge(baseConfig, {
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(root, 'index.html'), // 模板檔案
      inject: 'body' // js的script注入到body底部
    })
  ]
})

webpack常用外掛

  • extract-text-webpack-plugin 提取css到單獨的檔案
  • compression-webpack-plugin 壓縮gzip
  • webpack.optimize.UglifyJsPlugin 壓縮js檔案,內建外掛
  • webpack.DefinePlugin 定義全域性變數,內建外掛
  • webpack.optimize.CommonsChunkPlugin 提取公共依賴,內建外掛

根據專案需求新增相應的外掛,外掛配置引數請檢視官方文件,這裡不進行羅列

2、在package.json中新增指令碼:"build": "webpack --config webpack.config/pro.js"

3、執行npm run build,可以在dist資料夾中看到打包好的檔案。