從零開始搭建一個簡單的基於webpack的vue開發環境

深紅發表於2018-01-11

都8102年了,現在還來談webpack的配置,額,是有點晚了。而且,基於vue-cli或者create-react-app生成的專案,也已經一鍵為我們配置好了webpack,看起來似乎並不需要我們深入瞭解。

不過,為了學習和理解webpack解決了前端的哪些痛點,還是有必要從零開始自己搭建一個簡單的開發環境。本文的webpack配置參考了vue-cli提供webpack-simple模板,這也是vue-cli裡面最簡單的一個webpack配置,非常適合從零開始學習。

注: 本文webpack基於3.10.0

演示程式碼下載

安裝webpack

npm i webpack -g
複製程式碼

專案初始化

新建一個資料夾vue-webpack-simple

新建package.json

npm init -y
複製程式碼

安裝vue webpack webpack-dev-server

npm i vue --save
複製程式碼
npm i webpack webpack-dev-server --save-dev
複製程式碼

根目錄下新建index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
</body>
</html>
複製程式碼

根目錄下新建webpack.config.js

var path = require('path');
var webpack = require('webpack');

module.exports = {};
複製程式碼

新建src資料夾,src資料夾下新建main.js

目前整個專案的結構如下:

從零開始搭建一個簡單的基於webpack的vue開發環境

js模組化

在ES6出現之前,js是沒有統一的模組體系。 伺服器端使用CommonJS規範,而瀏覽器端又有AMD和CMD兩種規範

webpack的思想就是一切皆模組,官方推薦使用commonJS規範,這使得我們瀏覽器端也可以使用commonJS的模組化寫法

module.exports = {}
複製程式碼

src目錄下新建一個util.js

module.exports = function say() {
    console.log('hello world');
}
複製程式碼

main.js

var say = require('./util');
say();
複製程式碼

修改webpack.config.js

var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: './src/main.js', // 專案的入口檔案,webpack會從main.js開始,把所有依賴的js都載入打包
    output: {
        path: path.resolve(__dirname, './dist'), // 專案的打包檔案路徑
        publicPath: '/dist/', // 通過devServer訪問路徑
        filename: 'build.js' // 打包後的檔名
    },
    devServer: {
        historyApiFallback: true,
        overlay: true
    }
};
複製程式碼

修改package.josn

"scripts": {
    "dev": "webpack-dev-server --open --hot",
    "build": "webpack --progress --hide-modules"
  },
複製程式碼

注意:webpack-dev-server會自動啟動一個靜態資源web伺服器 --hot參數列示啟動熱更新

修改index.html,引入打包後的檔案

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script src="/dist/build.js"></script>
</body>

</html>
複製程式碼

執行

npm run dev
複製程式碼

可以發現瀏覽器自動開啟的一個頁面,檢視控制檯,有hello world打出

我們隨意修改util.js,可以發現瀏覽器會自動重新整理,非常方便。

如果我們希望看打包後的bundle.js檔案,執行

npm run build
複製程式碼

可以看到生成了一個dist目錄,裡面就有打包好後的bundle.js

從零開始搭建一個簡單的基於webpack的vue開發環境

webpack預設不支援轉碼es6,但是import export這兩個語法卻單獨支援。所以我們可以改寫前面的模組化寫法

util.js

export default function say() {
    console.log('hello world ');
}
複製程式碼

main.js

import say from './util';

say();

複製程式碼

引入vue

下面我們來試著引入vue(目前不考慮單檔案.vue)

main.js

import Vue from 'vue';

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
});

複製程式碼

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        {{message}}
    </div>
    <script src="/dist/build.js"></script>
    
</body>

</html>
複製程式碼

還要注意一點:要修改webpack.config.js檔案

var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: './src/main.js',
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'build.js'
    },
    devServer: {
        historyApiFallback: true,
        overlay: true
    },
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
        }
    }
};
複製程式碼

重新執行npm run dev,可以看到,頁面正常顯示了Hello World

引入scss和css

webpack預設只支援js的模組化,如果需要把其他檔案也當成模組引入,就需要相對應的loader解析器

npm i node-sass css-loader vue-style-loader sass-loader --save-dev
複製程式碼

webpack.config.js

var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: './src/main.js',
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'build.js'
    },
    devServer: {
        historyApiFallback: true,
        overlay: true
    },
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
        }
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'vue-style-loader',
                    'css-loader'
                ],
            }
        ]
    }
};
複製程式碼

解釋:

{
    test: /\.css$/,
    use: [
        'vue-style-loader',
        'css-loader'
    ],
}
複製程式碼

這段程式碼意思是:匹配字尾名為css的檔案,然後分別用css-loader,vue-style-loader去解析 解析器的執行順序是從下往上(先css-loader再vue-style-loader)

注意:因為我們這裡用vue開發,所以使用vue-style-loader,其他情況使用style-loader

css-loader使得我們可以用模組化的寫法引入css,vue-style-loader會將引入的css插入到html頁面裡的style標籤裡

要引入scss也是同理的配置寫法:

module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'vue-style-loader',
                    'css-loader'
                ],
            },
            {
                test: /\.scss$/,
                use: [
                    'vue-style-loader',
                    'css-loader',
                    'sass-loader'
                ],
            },
            {
                test: /\.sass$/,
                use: [
                    'vue-style-loader',
                    'css-loader',
                    'sass-loader?indentedSyntax'
                ],
            }]
    }
複製程式碼

我們現在來試下 在src目錄下新建style目錄,style目錄裡新建common.scss

body {
    background: #fed;
}
複製程式碼

main.js

import './style/common.scss';
複製程式碼

發現css樣式有用了

使用babel轉碼

ES6的語法大多數瀏覽器依舊不支援,bable可以把ES6轉碼成ES5語法,這樣我們就可以大膽的在專案中使用最新特性了

npm i babel-core babel-loader babel-preset-env babel-preset-stage-3 --save-dev
複製程式碼

在專案根目錄新建一個.babelrc檔案

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

複製程式碼

webpack.config.js新增一個loader

{
    test: /\.js$/,
    loader: 'babel-loader',
    exclude: /node_modules/
}
複製程式碼

exclude表示忽略node_modules資料夾下的檔案,不用轉碼

現在我們來試下async await語法吧 util.js

export default function getData() {
    return new Promise((resolve, reject) => {
        resolve('ok');
    })
}
複製程式碼

main.js

import getData from './util';
import Vue from 'vue';

import './style/common.scss';

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  },
  methods: {
    async fetchData() {
      const data = await getData();
      this.message = data;
    }
  },
  created() {
    this.fetchData();
  }
});
複製程式碼

這時控制檯會報一個錯誤regeneratorRuntime is not defined,因為我們沒有安裝babel-polyfill

npm i babel-polyfill --save-dev
複製程式碼

然後修改webpack.config.js的入口

entry: ['babel-polyfill', './src/main.js'],
複製程式碼

重新npm run dev,可以發現正常執行了

引入圖片資源

把圖片也當成模組引入

npm i file-loader --save-dev
複製程式碼

webpack.config.js新增一個loader

{
    test: /\.(png|jpg|gif|svg)$/,
    loader: 'file-loader',
    options: {
        name: '[name].[ext]?[hash]'
    }
}
複製程式碼

在src目錄下新建一個img目錄,存放一張圖片logo.png

修改main.js

import getData from './util';
import Vue from 'vue';

import './style/common.scss';


Vue.component('my-component', {
  template: '<img :src="url" />',
  data() {
    return {
      url: require('./img/logo.png')
    }
  }
})

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue !'
  },
  methods: {
    async fetchData() {
      const data = await getData();
      this.message = data;
    }
  },
  created() {
    this.fetchData()
  }
});

複製程式碼

修改index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        {{message}}
        <my-component/>
    </div>
    <script src="/dist/build.js"></script>
    
</body>

</html>

複製程式碼

可以看見,圖片也被正確載入了

單檔案元件

在前面的例子裡,我們使用 Vue.component 來定義全域性元件 在實際專案裡,更推薦使用單檔案元件

npm i vue-loader vue-template-compiler --save-dev
複製程式碼

新增一個loader

{
    test: /\.vue$/,
    loader: 'vue-loader',
    options: {
        loaders: {
            'scss': [
                'vue-style-loader',
                'css-loader',
                'sass-loader'
            ],
            'sass': [
                'vue-style-loader',
                'css-loader',
                'sass-loader?indentedSyntax'
            ]
        }
    }
}
複製程式碼

在src目錄下新建一個App.vue

<template>
  <div id="app">
    <h1>{{ msg }}</h1>
    <img src="./img/logo.png">
    <input type="text" v-model="msg">
  </div>
</template>

<script>

import getData from './util';

export default {
  name: 'app',
  data () {
    return {
      msg: 'Welcome to Your Vue.js'
    }
  },
  created() {
    this.fetchData();
  },
  methods: {
     async fetchData() {
      const data = await getData();
      this.msg = data;
    }
  }
}
</script>

<style lang="scss">
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;

  h1 {
    color: green;
  }
}
</style>

複製程式碼

main.js

import Vue from 'vue';
import App from './App.vue';

import './style/common.scss';

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

複製程式碼

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app"></div>
    <script src="/dist/build.js"></script>
</body>

</html>
複製程式碼

npm run dev,可以發現單檔案被正確載入了

source-map

在開發階段,除錯也是非常重要的一項需求。

App.vue

created() {
    this.fetchData();
    console.log('23333');
}
複製程式碼

我們故意打一個console,開啟控制檯

從零開始搭建一個簡單的基於webpack的vue開發環境

我們點選進入這個console的詳細地址

從零開始搭建一個簡單的基於webpack的vue開發環境

進入的是打包後的build.js,我並不知道是在哪個元件裡寫的,這就造成了除錯困難

這時就要修改webpack.config.js

module.exports = {
    entry: ['babel-polyfill', './src/main.js'],
    // 省略其他...

    devtool: '#eval-source-map'
};
複製程式碼

重新npm run dev

從零開始搭建一個簡單的基於webpack的vue開發環境
這次除錯,它直接返回那個元件的原始碼了,這不是被打包過的!

打包釋出

我們先試著npm run build打包一下檔案

從零開始搭建一個簡單的基於webpack的vue開發環境

會發現,打包後的build.js非常大,有500多k了

在實際釋出時,會對檔案進行壓縮,快取,分離等等優化處理

npm i cross-env --save-dev
複製程式碼

修改package.json

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

這次我們設定了環境變數,打包時,NODE_ENV是production

然後修改webpack.config.js,判斷NODE_ENV為production時,壓縮js程式碼

var path = require('path');
var webpack = require('webpack');

module.exports = {
    // 省略...
}

if (process.env.NODE_ENV === 'production') {
    module.exports.devtool = '#source-map';
    module.exports.plugins = (module.exports.plugins || []).concat([
      new webpack.DefinePlugin({
        'process.env': {
          NODE_ENV: '"production"'
        }
      }),
      new webpack.optimize.UglifyJsPlugin(),
    ])
  }
  
複製程式碼

重新打包

從零開始搭建一個簡單的基於webpack的vue開發環境

可以看見,壓縮效果非常明顯!

至此,一個非常簡單的vue開發環境搭建成功。

注意:本文中的配置還有非常多可以優化的地方,比如分離js和css

讀者可以自行了解相關知識,這裡只是帶領大家瞭解最基礎的webpack配置。

相關文章