1. 程式人生 > >webpack4搭建Vue開發環境筆記~~持續更新

webpack4搭建Vue開發環境筆記~~持續更新

專案git地址

一、node知識

__dirname: 獲取當前檔案所在路徑,等同於path.dirname(__filename)


console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr

path.resolve([..paths]): 把一個路徑或路徑片段的序列解析為一個絕對路徑

  • 給定的路徑的序列是從右往左被處理的,後面每個 path 被依次解析,直到構造完成一個絕對路徑
  • 如果處理完全部給定的 path 片段後還未生成一個絕對路徑,則當前工作目錄會被用上
  • 生成的路徑是規範化後的,且末尾的斜槓會被刪除,除非路徑被解析為根目錄
  • 長度為零的 path 片段會被忽略
  • 如果沒有傳入 path 片段,則 path.resolve() 會返回當前工作目錄的絕對路徑

path.resolve('/foo/bar', './baz');
// 返回: '/foo/bar/baz'

path.resolve('/foo/bar', '/tmp/file/');
// 返回: '/tmp/file'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// 如果當前工作目錄為 /home/myself/node,
// 則返回 '/home/myself/node/wwwroot/static_files/gif/image.gif'

二、配置最基本的webpack

專案目錄生成如下檔案


.
├── build
│   ├── build.js
│   ├── index.html
│   ├── webpack.base.conf.js
│   ├── webpack.dev.conf.js
│   └── webpack.prod.conf.js
├── package.json
├── package-lock.json
└── src
    ├── App.vue
    ├── main.js
    ├── timg.gif
    └── timg.jfif

首先,先裝下webpack依賴:


npm i webpack webpack webpack-cli -D

1、webpack.base.conf.js


const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: {
    bundle: path.resolve(__dirname, '../src/main.js')
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].[hash].js',
    publicPath: '/'
  },
  module: {
    rules: [
      
    ]
  },
  plugins: [
    <!-- 以當前目錄的index.html為模板生成新的index.html,這個外掛就是將新生成的檔案(js,css)引入 -->
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'index.html')
    })
  ],
  resolve: {
    
  }
};

上面用到了html-webpack-plugin外掛,裝下:


npm i html-webpack-plugin -D

2、webpack.dev.conf.js


const merge = require('webpack-merge');
const path = require('path');
const baseConfig = require('./webpack.base.conf');
module.exports = merge(baseConfig, {
  // mode關係到程式碼壓縮質量  https://webpack.docschina.org/guides/tree-shaking/
  mode: 'development',
  // source-map,將編譯後的程式碼對映到原始碼,便於報錯後定位錯誤
  devtool: 'inline-source-map',
  <!-- webpack-dev-server配置項 -->
  devServer: {
    <!-- devserver啟動服務的根路徑 -->
    contentBase: path.resolve(__dirname, '../dist'),
    <!-- 開啟瀏覽器 -->
    open: true
  }
});

合併webpack配置的外掛webpack-merge,能夠啟一個簡易服務的webpack-dev-server,詳情


npm i webpack-dev-server webpack-merge -D

3、webpack.prod.conf.js


const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const path = require('path');
const baseConfig = require('./webpack.base.conf');
module.exports = merge(baseConfig, {
  mode: 'production',
  devtool: 'source-map',
  module: {
    rules: []
  },
  plugins: [
    new CleanWebpackPlugin(['dist/'], {
      root: path.resolve(__dirname, '../')
    })
  ]
});

清除檔案的外掛:


npm i clean-webpack-plugin -D

4、build.js


const webpack = require('webpack');
const config = require('./webpack.prod.conf');

webpack(config, (err, stats) => {
  if (err || stats.hasErrors()) {
    // 在這裡處理錯誤
    console.error(err);
    return;
  }
  // 處理完成
  console.log(stats.toString({
    chunks: false,  // 使構建過程更靜默無輸出
    colors: true    // 在控制檯展示顏色
  }));
});

5、npm scripts


// package.json
{
  +++
  "scripts": {
    "build": "node build/build.js",
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
  },
}

以上算是一個webpack的基本結構,如果入口檔案(main.js)裡引入的是正經js,npm dev和npm build是可以的打包編譯的,但是我們是要寫vue,那就要加些loader和plugins了

三、引入一些基本的loader

1、babel-loader

依賴安裝要求:webpack 4.x | babel-loader 7.x | babel 6.x,注意babel-loader和babel的版本,不然會報錯


npm install -D [email protected] babel-core babel-preset-env webpack

然後再配置中加入


// base.conf.js
module.exports = {
  +++
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        }
      },
      +++
    ]
  }
}

我們還需要新增一個配置檔案(.babelrc)在根目錄下:


/// .babelrc
{
  "presets": [
    ["env", {
      "targets": {
        "browsers": [">0.25%", "last 2 versions", "not ie 11", "not op_mini all"]
      }
    }]
  ]
}

這就是 babel-preset-env 的作用,幫助我們配置 babel。我們只需要告訴它我們要相容的情況(目標執行環境),它就會自動把程式碼轉換為相容對應環境的程式碼。
以上程式碼表示我們要求程式碼相容最新兩個版本的瀏覽器,不用相容 11(及以下)和Opera Mini,另外市場份額超過 0.25% 的瀏覽器也必須支援。
只需要告訴 babel-preset-env 你想要相容的環境,它就會自動轉換

2、url-loader、file-loader

如果我們希望在頁面引入圖片(包括img的src和background的url)。當我們基於webpack進行開發時,引入圖片會遇到一些問題

其中一個就是引用路徑的問題。拿background樣式用url引入背景圖來說,我們都知道,webpack最終會將各個模組打包成一個檔案,因此我們樣式中的url路徑是相對入口html頁面的,而不是相對於原始css檔案所在的路徑的。這就會導致圖片引入失敗。這個問題是用file-loader解決的,file-loader可以解析專案中的url引入(不僅限於css),根據我們的配置,將圖片拷貝到相應的路徑,再根據我們的配置,修改打包後文件引用路徑,使之指向正確的檔案

另外,如果圖片較多,會發很多http請求,會降低頁面效能。這個問題可以通過url-loader解決。url-loader會將引入的圖片編碼,生成dataURl。相當於把圖片資料翻譯成一串字元。再把這串字元打包到檔案中,最終只需要引入這個檔案就能訪問圖片了。當然,如果圖片較大,編碼會消耗效能。因此url-loader提供了一個limit引數,小於limit位元組的檔案會被轉為DataURl,大於limit的還會使用file-loader進行copy。

url-loader和file-loader是什麼關係呢?簡答地說,url-loader封裝了file-loader。url-loader賴於file-loader,即使用url-loader時,也要安裝file-loader


npm i url-loader file-loader -D

/// base.conf.js
module.exports = {
  +++
  module: {
    rules: [
      +++
      {
        test: /\.(png|jpg|jfif|jpeg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              // 低於這個limit就直接轉成base64插入到style裡,不然以name的方式命名存放
              // 這裡的單位時bit
              limit: 8192,
              name: 'static/images/[hash:8].[name].[ext]'
            }
          }
        ]
      },
      // 字型圖示啥的,跟圖片分處理方式一樣
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'url-loader',
            name: 'static/font/[hash:8].[name].[ext]'
          }
        ]
      },
    ]
  },
}

3、vue-loader

作用自己去看


npm i vue-loader -D

// base.conf.js
module.exports = {
  +++
  module: {
    rules: [
      +++
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  }
}

在這裡還要一個外掛,這個外掛是必須的!


// base.conf.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
  +++
  plugins: [
      // 它的職責是將你定義過的其它規則複製並應用到 .vue 檔案裡相應語言的塊。
      // 例如,如果你有一條匹配 /\.js$/ 的規則,那麼它會應用到 .vue 檔案裡的 <script> 塊
      new VueLoaderPlugin(),
      +++
  ]
}

4、處理樣式

  • less-loader: 將less轉css
  • css-loader: 將css轉為CommonJS規範的js字串
  • style-loader: 將js字串轉為style node插入到html中
  • postcss-loader: PostCSS 是一個允許使用 JS 外掛轉換樣式的工具,我們用postcss的外掛就要配置它,autoprefixer就是postcss專案裡的一個外掛
  • autoprefixer: 添加了 vendor 瀏覽器字首,它使用 Can I Use 上面的資料。

npm i less-loader css-loader style-loader less autoprefixer postcss-loader -D

const StyleLintPlugin = require('stylelint-webpack-plugin');
// base.conf.js
module.exports = {
  +++
  module: {
    rules: [
      {
        // less css
        test: /\.l?css$/,
        // use裡的loader執行順序為從下到上,loader的順序要注意
        // 這裡檢測到less/css檔案後需要將後續處理loader都寫在此use裡,如果less和css過分開檢測處理,不能說先用less-loader轉成css,然後讓它走/\.css/裡的use
        use: [
          {loader: 'style-loader'},
          {loader: 'css-loader'},
          {loader: 'postcss-loader'},
          {loader: 'less-loader'},
        ]
      },
      +++
    ]
  }
}
  • 配置postcss
在根目錄新建個postcss.config.js檔案來配置autoprefixer,通過 Browerslist來幫助你配置,瀏覽器市場份額,瞭解下 browserl.ist

module.exports = {
  plugins: [
    require('autoprefixer')({
      "browsers": [
        "defaults",
        "not ie < 9",
        "last 2 versions",
        "> 1%",
        "iOS 7",
        "last 3 iOS versions"
      ]
    })
  ]
}
  • mini-css-extract-plugin提取css
這裡打包 cssless 為例,r如果要用 mini-css-extract-plugin外掛提取 css,將上面改為如下:

npm install mini-css-extract-plugin -D

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
+++
module.exports = {
  +++
  // 模組,loader
  module: {
    rules: [
      +++
      {
        test: /\.l?(c|e)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {loader: 'css-loader'},
          {loader: 'postcss-loader'},
          {loader: 'less-loader'},
        ]
      },
      +++
    ]
  },
  // 外掛
  plugins: [
    +++
    new MiniCssExtractPlugin({
      filename: 'static/css/[name].[hash].css',
      chunkFilename: 'static/css/[name].[hash].css'
    })
  ]
}

5、然後配置下別名resolve.extensions


// base.conf.js
module.exports = {
  +++
  resolve: {
    alias: {
      // 配置別名'vue$',不然import 'vue'時,webpack找不到
      'vue$': 'vue/dist/vue.esm.js',
      // 這個為src配置別名,非必需,為方便而已
      '@': path.resolve(__dirname, '../src')
    },
    // 在import這些拓展名的檔案時,可以省略拓展名
    extensions: ['*', '.js', '.json', '.vue'],
  }
}

現在我們來測試以下,安裝一個vue


npm i vue

在mian.js裡面


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

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

在App.vue裡寫入


<template>
  <div>
    <h3 class="title">{{title}}</h3>
    <p class="content">{{content}}</p>
    <div class="goddess">
      <div class="right">
        <h4 class="right__h4">background引入圖片</h4>
        <div class="right__img"></div>
      </div>
      <div class="left">
        <h4 class="left__h4">img標籤直接引入圖片</h4>
        <img class="left__img" src="./timg.jfif" />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  components: {
    Foot: Footer
  },
  data () {
    return {
      title: 'hello word',
      content: 'webpack4 搭建vue環境',
    }
  },
}
</script>


<style lang="less" scoped>
  .title {
    font-size: 20px;
    text-align: center;
    color: red;
  }
  .content {
    font-size: 14px;
    color: #333333;
  }
  .goddess {

    .left {
      margin-left: 50%;
      &__h4 {
        font-size: 14px;
      }
      &__img {
        width: 308px;
        height: 433px;
      }
    }
    .right {
      float: left;
      &__h4 {
        font-size: 14px;
      }
      &__img {
        width: 300px;
        height: 150px;
        background: url('./timg.gif') no-repeat;
      }
    }
  }
</style>

好了,npm dev 先看一下女神,放鬆一下:

在這裡插入圖片描述

四、做一些優化

1、提取公共程式碼

使用 splitChucksPlugin 外掛,這是 Webpack 自帶的,不用安裝第三方依賴,預設配置即可


<!-- base.conf.js -->
module.exports = {
  +++
  plugins: [
    +++
    new webpack.optimize.SplitChunksPlugin()
  ]
}

想了解這個外掛的預設配置及如何配置,英文中文

2、將第三方庫單獨打包

每次我們對專案進行打包時,我們都會把引用的第三方依賴給打包一遍,比如 Vue、Vue-Router、React 等等。但是這些庫的程式碼基本都是不會變動的,我們沒必要每次打包都構建一次,所以我們最好將這些第三方庫提取出來單獨打包,這樣有利於減少打包時間。
官方外掛是 DllPlugin。推薦一個比較好用的外掛 —— autodll-webpack-plugin


npm i autodll-webpack-plugin -D

// base.conf.js
module.exports = {
  +++
  plugins: [
   // 將一些不太可能改動的第三方庫單獨打包,會通過快取極大提升打包速度
    new AutoDllPlugin({
      // will inject the DLL bundle to index.html
      // default false
      inject: true,
      debug: false,
      filename: '[name]_[hash].js',
      path: 'static',
      entry: {
        // [name] = vue, 在這裡會將entry裡的每個item(vue,jquery)都打包成一個js
        vue: [
          'vue',
          'vue-router'
        ],
        // [name] = jquery
        // jquery: [
        //   'jquery',
        //   'jquery-from'
        // ]
      }
    }),
    +++
  ]
}

inject 為 true,外掛會自動把打包出來的第三方庫檔案插入到 HTML。filename 是打包後文件的名稱。path 是打包後的路徑。entry 是入口,vendor 是你指定的名稱,陣列內容就是要打包的第三方庫的名稱,不要寫全路徑,Webpack 會自動去 node_modules 中找到的。
每次打包,這個外掛都會檢查註冊在 entry 中的第三方庫是否發生了變化,如果沒有變化,外掛就會使用快取中的打包檔案,減少了打包的時間,這時 Hash 也不會變化。

3、熱過載

“熱過載”不只是當你修改檔案的時候簡單重新載入頁面。啟用熱過載後,當你修改 .vue 檔案時,該元件的所有例項將在不重新整理頁面的情況下被替換。它甚至保持了應用程式和被替換元件的當前狀態!當你調整模版或者修改樣式時,這極大地提高了開發體驗,以下兩種方式擇一即可


"scripts": {
  +++
  "dev": "webpack-dev-server --hot --inline --progress --config build/webpack.dev.conf.js"
},
  • 方式2:或者通過配置webpack.dev.config.js,相比第一種,就會麻煩一點

const webpack = require('webpack')
module.exports = {
  +++
  module: {
    devServer: {
      +++
      // 開啟熱過載
      hot: true
    },
    plugins: [
      // 啟用模組熱替換(HMR)
      new webpack.HotModuleReplacementPlugin(),
      // 當開啟 HMR 的時候使用該外掛會顯示模組的相對路徑,建議用於開發環境。
      new webpack.NamedModulesPlugin(),
      +++
    ]
  }
}

4、eslint

確保 VS Code 安裝了 Vetur(設定編輯器支援vue檔案,如果寫過vue忽略)Eslint 外掛


npm i -g [email protected]

eslint --init

然後選個最流行的就行了

會幫你新建一個.eslintrc.js 的配置檔案以及裝一些 eslint 的依賴

在 package.json 里加上:


{
  +++
  "scripts": {
    +++
    "lint": "eslint --ext .js,.vue src"
  },
}

你可以嘗試的npm run lint,你會發現spacing 系列no-new.vue語法不支援等問題
當然,你可以通過改寫 lint 命令(加個--fix)來解決部分語法報錯


{
  +++
  "scripts": {
    +++
    "lint": "eslint --fix --ext .js,.vue src"
  },
}

或者儲存的時候讓 eslint 外掛自動修復。 更改 VS Code 中的 eslint.autoFixOnSave 設定,勾選文字編輯->Format On Save
當然這玩法不是我們這裡的重點,安裝 eslint-plugin-html 來解決vue語法eslint報錯問題


npm install -D eslint-plugin-html

在.eslintrc.js 中配置 eslint-plugin-html


module.exports = {
    +++
    "plugins": [
      // 使用eslint-plugin-html
      "html"
    ]
};

至於 main.js 裡的 new 指定給變數錯誤 disable 掉


+++
/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: {
    App
  },
  template: '<App/>'
})
  • 程式碼如果eslint有報錯,就讓編譯不通過

npm i eslint-loader babel-eslint -D

<!-- .eslintrc.js -->
{
  +++
  "parser": "babel-eslint"
}

<!-- base.config.js -->
module.exports = {
  module: {
    rules: [
      {
        test: /\.(vue|js)$/,
        loader: 'eslint-loader',
        exclude: /node_modules/,
        // 預處理
        enforce: 'pre',
        include: [path.join(__dirname, '..', 'src')]
      }
    ]
  }
}
  • 程式碼提交之前對程式碼進行檢查

npm i husky -D 

<!-- package.json -->
{
  +++
  "script": {
    +++
    "precommit": "eslint --fix --ext .js --ext .vue src/"
  }
}

該工具可以在我們提交程式碼時,呼叫"precommit"鉤子,執行預處理操作,eslint不通過,無法提交

在提交時僅對git add的 js,vue 檔案進行檢測lint-staged 和 husky 在 pre-commit 階段做程式碼檢查


npm i lint-staged -D

<!-- package.json -->
{
  +++
  "script": {
    +++
    "precommit": "lint-staged"
  },
  "lint-staged": {
    "src/**/*.{js,vue}": [
      "eslint --fix",
      "git add"
    ]
  },
}

5、引入jquery shimming


npm i juery -D

<!-- base.config.js -->
 module.exports = {
   +++
   plugins: [
     +++
     new webpack.ProvidePlugin({
       $: 'jquery'
     })
   ]
  };

這樣就可以將$當全域性變數使用了,當然eslint要配置個global,這裡不介紹了

對你有幫助的話點個刷波6,點個贊吧

原文地址:https://segmentfault.com/a/1190000016972438