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

原文出處: https://segmentfault.com/a/11...css

原文做者: 深紅html

這篇文章對初次搭建webpack的我頗有啓發,但原文因爲版本問題,存在一些小小的錯誤,根據原文下方的評論作了修改後,算是成功完成了,如今我就從新把這篇文章梳理一遍。前端


正文:
都8102年了,如今還來談webpack的配置,額,是有點晚了。並且,基於vue-cli或者create-react-app生成的項目,也已經一鍵爲咱們配置好了webpack,看起來彷佛並不須要咱們深刻了解。vue

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

演示代碼下載

安裝webpack

npm i webpack -g

webpack4 還要單獨安裝react

npm i webpack-cli -g

項目初始化

新建一個文件夾vue-webpack-simplewebpack

新建package.jsongit

npm init -y

在目錄下安裝webpack-clies6

npm i webpack-cli --save

安裝vue webpack webpack-dev-servergithub

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

目前整個項目的結構以下:

clipboard.png

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

clipboard.png

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: red;
}

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在rules添加一個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在rules添加一個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

在rules添加一個loader

{
    test: /\.vue$/,
    loader: 'vue-loader',
}

引入plugins

const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports= {
   // ...省略
    plugins: [
    new VueLoaderPlugin()
  ]
}

在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,打開控制檯

clipboard.png

咱們點擊進入這個console的詳細地址

clipboard.png

進入的是打包後的build.js,我並不知道是在哪一個組件裏寫的,這就形成了調試困難
這時就要修改webpack.config.js

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

    devtool: '#eval-source-map'
};

從新npm run dev

clipboard.png
此次調試,它直接返回那個組件的源代碼了,這不是被打包過的!

打包發佈

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

在實際發佈時,會對文件進行壓縮,緩存,分離等等優化處理

npm i cross-env --save-dev

修改package.json

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

此次咱們設置了環境變量,打包時,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"'
        }
      })
    ])
  }

從新打包

clipboard.png

能夠看見,壓縮效果很是明顯!

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

注意:本文中的配置還有很是多能夠優化的地方,好比分離js和css

讀者能夠自行了解相關知識,這裏只是帶領你們瞭解最基礎的webpack配置。

相關文章
相關標籤/搜索