從零構建到優化一個相似vue-cli的腳手架

前言

想必大多數人在開發 vue 等 SPA 項目都時候都會直接用 vue-cli 等腳手架開發,一是方便省去了好多配置上的功夫,二是 vue-cli 畢竟是久經考驗較爲成熟的東西,遇到問題也能在網上找到相應解決方案。css

可是,若是咱們要更好地理解腳手架的配置及其構建打包的機制,咱們就有必要從零開始,依葫蘆畫瓢本身配置一個相似於 vue-cli 這樣的項目了。在此,我作了如下簡單配置,請各位大佬批評指正,並誠心但願能獲得大佬的指點,解決文章最後關於 Tree Shaking 致使打包缺失 css 的問題。html

本文主要包括以下配置:前端

  • 區分環境變量及合併配置
  • webpack-dev-server 本地服務器
  • HtmlWebpackPlugin 生成 html
  • CleanWebpackPlugin 清理文件夾
  • loaderbabel 配置
  • HotModuleReplacementPlugin 熱更新
  • postcss-loader 增長 css 前綴
  • vue SPA 引入及解析
  • vue-router 安裝與使用
  • mini-css-extract-plugin 分離 css
  • purifycss-webpack purify-css 消除冗餘 css
  • optimize-css-assets-webpack-plugin 壓縮 css
  • terser-webpack-plugin 壓縮 js
  • splitChunks 提取公共代碼
  • image-webpack-loader 圖片壓縮
  • gZip 加速優化

項目源碼:github.com/Michael-lzg…vue

若是對 webpack 基本配置還不瞭解的小夥伴,可查看如下文章
從零開始構建一個 webpack 項目
搭建一個 vue-cli4+webpack 移動端框架(開箱即用)java

廢話很少說,老司機帶你馬上上路。node

搭建 webpack 項目框架

構建項目結構

  1. 建立 webpack-vue-cli 文件夾,npm-init-y 初始化項目webpack

  2. 安裝 webpack 相關依賴git

npm i webpack webpack-cli webpack-dev-server webpack-merge --save-dev
複製代碼

若是 webpackwebpack-cli 沒有全局安裝的話,要先全局安裝github

  1. 創建項目文件夾
├── src   // webpack配置文件
    |——main.js  // 入口文件
├── static   // 項目打包路徑
├── index.html   // 模板html
├── webpack.base.js   // 打包基本配置
├── webpack.dev.js   // 本地環境配置
├── webpack.prod.js   // 生產環境配置
複製代碼

index.htmlmain.js 的代碼很少說,直接進入 webpack 配置環節。web

區分環境

爲了更好的優化打包,咱們將 webpack 的配置分開開發環境和生產環境。

  • webpack.base.js 公共配置文件
  • webpack.dev.js 開發環境的配置文件
  • webpack.prod.js 生產環境的配置文件

webpack.dev.jswebpack.prod.js,咱們能夠利用 webpack-merge 進行配置的合併。

而後,咱們在 package.json 定義不一樣環境的打包命令

"scripts": {
  "dev": "webpack-dev-server --config webpack.dev.js --mode development",
  "build": "webpack --config webpack.prod.js"
}
複製代碼

公共配置

咱們先來看一下 webpack.base.js 的公共配置,定義好入口文件和出口文件

module.exports = {
  entry: {
    index: path.join(__dirname, '/src/main.js'),
  },
  output: {
    path: path.join(__dirname, '/dist'), //打包後的文件存放的地方
    filename: 'js/[name].[hash].js', // 每次保存 hash 都變化
  },
}
複製代碼

webpack-dev-server

webpack 提供了一個可選的本地開發服務器,這個本地服務器基於 node.js 構建,因此在 webpack.dev.js 進行配置

const merge = require('webpack-merge') // 引入webpack-merge功能模塊
const common = require('./webpack.base.js') // 引入webpack.common.js

module.exports = merge(common, {
  // 將webpack.common.js合併到當前文件
  devServer: {
    contentBase: './dist', // 本地服務器所加載文件的目錄
    port: '8899', // 設置端口號爲8088
    inline: true, // 文件修改後實時刷新
    historyApiFallback: true, //不跳轉
    hot: true, // 熱更新
  },
  mode: 'development', // 設置mode
})
複製代碼

HtmlWebpackPlugin

HtmlWebpackPlugin 簡化了 HTML 文件的建立,它能夠根據 html 模板在打包後自動爲你生產打包後的 html 文件。這對於在文件名中包含每次會隨着編譯而發生變化哈希的bundle

plugins: [
  new HtmlWebpackPlugin({
    template: path.join(__dirname, '/index.html'), // new一個這個插件的實例,並傳入相關的參數
  }),
]
複製代碼

至此就搭建好一個乞丐版的 webpack 項目了,你能夠隨意編寫代碼,分別在開發環境和生產環境執行命令查看效果。

loader 配置

loader 可讓 webpack 可以去處理那些非 javaScript 文件(webpack 自身只理解 javaScript)。loader 能夠將全部類型的文件轉換爲 webpack 可以處理的有效模塊,而後你就能夠利用 webpack 的打包能力,對它們進行處理。

對於 loader 的科普和配置,在這裏不作一一說明,直接奉上代碼,分別是處理樣式,js 和文件的 loader

module: {
  rules: [
    {
      test: /\.css$/, // 正則匹配以.css結尾的文件
      use: ['style-loader', 'css-loader'],
    },
    {
      test: /\.less$/,
      use: ['style-loader', 'css-loader', 'less-loader'],
    },
    {
      test: /\.js$/,
      loader: 'babel-loader',
      include: [resolve('src')],
    },
    {
      test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
      loader: 'url-loader',
      options: {
        limit: 10000,
        name: utils.assetsPath('img/[name].[hash:7].[ext]'),
      },
    },
  ]
}
複製代碼

爲了更方便的配置和優化 babel-loader,咱們能夠將其提取出來,在根目錄下新建 .babelrc 文件

{
  "presets": ["env"]
}
複製代碼

CleanWebpackPlugin

在每次構建前清理/dist 文件夾,生產最新的打包文件,這時候就用到 CleanWebpackPlugin 插件了。

plugins: [
  new HtmlWebpackPlugin({
    template: path.join(__dirname, '/index.html'), // new一個這個插件的實例,並傳入相關的參數
  }),
  new CleanWebpackPlugin(), // 所要清理的文件夾名稱
]
複製代碼

HotModuleReplacementPlugin

HotModuleReplacementPlugin(HMR)是一個很實用的插件,能夠在咱們修改代碼後自動刷新預覽效果,在開發環境使用。

  1. devServer 配置項中設置 hot: true

  2. HotModuleReplacementPlugin 是 webpack 模塊自帶的,因此引入 webpack 後,在 plugins 配置項中直接使用便可。

plugins: [
  new webpack.HotModuleReplacementPlugin(), // 熱更新插件
]
複製代碼

增長 css 前綴

平時咱們寫 css 時,一些屬性須要手動加上前綴,好比-webkit-border-radius: 10px;,在 webpack 中咱們可讓他自動加上

  1. 安裝依賴
npm i postcss-loader autoprefixer -D
複製代碼
  1. 在項目根目錄下新建 postcss.config.js 文件
module.exports = {
  plugins: [
    require('autoprefixer'), // 引用autoprefixer模塊
  ],
}
複製代碼
  1. 修改樣式 loader
rules: [
  {
    test: /\.css$/, // 正則匹配以.css結尾的文件
    use: ['style-loader', 'css-loader', 'postcss-loader'],
  },
  {
    test: /\.less$/,
    use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'],
  },
]
複製代碼

至此,一個 webpack 項目基本搭建而成,下面介紹 vue 的引用和項目優化。

搭建 vue SPA 模板

vue SPA

一、搭建一個相似於 vue-cli 的腳手架,首先咱們來依葫蘆畫瓢,在 main.js 寫上一下代碼

import Vue from 'vue'
import App from './App.vue'
new Vue({
  el: '#app',
  render: (h) => h(App),
})
複製代碼

二、而後在 src 文件夾下新建 APP.vue

<div id="app">SPA項目</div>
複製代碼

三、安裝相關依賴

到這裏,咱們 npm run dev 試一下就報錯了。由於咱們沒有安裝相關依賴,下面咱們下來安裝一下依賴

npm install vue vue-loader vue-template-compiler -D
複製代碼
  • vue: vue 的源碼
  • vue-loader:解析.vue 文件
  • vue-template-compiler: 編譯 vue

四、在 webpack 配置 vue-loader

const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
    ],
  },
  plugins: [new VueLoaderPlugin()],
}
複製代碼

vue-router

一、安裝依賴

npm install vue-router -D
複製代碼

二、在 src 文件夾下新建 router/index

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const router = new Router({
  routes: [
    {
      path: '/',
      component: () => import('../views/Home.vue'),
    },
    {
      path: '/admin',
      component: () => import('../views/admin.vue'),
    },
  ],
})

export default router
複製代碼

三、在 main.js 引用

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

new Vue({
  el: '#app',
  router,
  render: (h) => h(App),
})
複製代碼

就這樣,一個相似於 vue-cli 的腳手架就搭建好了,你能夠愉快地寫 .vue 文件進行 SPA 開發。

webpack 優化打包

分離 css

雖然 webpack 的理念是把 css、js 全都打包到一個文件裏,但要是咱們想把 css 分離出來,這裏咱們用到 mini-css-extract-plugin。對比另外一個插件 extract-text-webpack-plugin,它有如下優勢:

  • 異步加載
  • 不重複編譯,性能更好
  • 更容易使用
  • 只針對 CSS

可是mini-css-extract-plugin 不支持 HMR,因此咱們只能在生產環境使用它。

一、安裝依賴

npm install mini-css-extract-plugin -D
複製代碼

二、在webpack.prod.js 配置 loaderplugin

module: {
  rules: [
    {
      test: /\.(le|c)ss$/,
      use: [
        {
          loader: MiniCssExtractPlugin.loader,
          options: {
            publicPath: '../'
          },
        },
        'css-loader',
        'postcss-loader',
        'less-loader',
      ]
    }
  ]
},
plugins: [
  new MiniCssExtractPlugin({
    filename: "css/[name].[contenthash:8].css",
    chunkFilename: 'css/[id].[contenthash:8].css'
  })
]
複製代碼

分離 css 須要將 css loader 中的 style-loader 替換爲 MiniCssExtractPlugin

消除冗餘 css

有時候咱們 css 寫得多了或者重複了,這就形成了多餘的代碼,咱們但願在生產環境進行去除。

一、安裝依賴

npm i purifycss-webpack purify-css glob -D
複製代碼

二、webpack.prod.js 配置

const path = require('path')
const PurifyCssWebpack = require('purifycss-webpack') // 引入PurifyCssWebpack插件
const glob = require('glob') // 引入glob模塊,用於掃描所有html文件中所引用的css

module.exports = merge(common, {
  plugins: [
    new PurifyCssWebpack({
      paths: glob.sync(path.join(__dirname, 'src/*.html')),
    }),
  ],
})
複製代碼

壓縮 css

咱們但願減少 css 打包後的體積,能夠用到 optimize-css-assets-webpack-plugin。 一、安裝依賴

npm install optimize-css-assets-webpack-plugin -D
複製代碼

二、webpack.prod.js 配置

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin") // 壓縮css代碼

optimization: {
  minimizer: [
    // 壓縮css
    new OptimizeCSSAssetsPlugin({})
  ]
複製代碼

壓縮 js

Webpack4.0 默認是使用 terser-webpack-plugin 這個壓縮插件,在此以前是使用 uglifyjs-webpack-plugin,二者的區別是後者對 ES6 的壓縮不是很好,同時咱們能夠開啓 parallel 參數,使用多進程壓縮,加快壓縮。

一、安裝依賴

npm install terser-webpack-plugin -D
複製代碼

二、webpack.prod.js 配置

const TerserPlugin = require('terser-webpack-plugin') // 壓縮js代碼

optimization: {
  minimizer: [
    new TerserPlugin({
      parallel: 4, // 開啓幾個進程來處理壓縮,默認是 os.cpus().length - 1
      cache: true, // 是否緩存
      sourceMap: false,
    }),
    // 壓縮css
    new OptimizeCSSAssetsPlugin({}),
  ]
}
複製代碼

提取公共代碼

在用 webpack 打包的時候,對於一些不常常更新的第三方庫,好比 vue 全家桶的一些東西, 咱們但願能和本身的代碼分離開。webpack4 使用 splitChunks 的方法進行配置。

optimization: {
  // 分離chunks
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendor: {
        name: "vendor",
        test: /[\\/]node_modules[\\/]/,
        priority: 10,
        chunks: "initial" // 只打包初始時依賴的第三方
      },
    }
  }
}
複製代碼

圖片壓縮

在項目中有些圖片太大影響加載,咱們用 image-webpack-loader 進行壓縮。

一、安裝依賴

npm install image-webpack-loader -D
複製代碼

二、配置 loader

{
  test: /\.(png|jpg|svg|gif)$/,
  use: [
    {
      loader: 'url-loader',
      options: {
        esModule: false,
        limit: 1000,  // 限制只有小於1kb的圖片才轉爲base64
        outputPath: 'images', // 設置打包後圖片存放的文件夾名稱
        name: '[name][hash:8].[ext]'
      }
    },
    {
      loader: 'image-webpack-loader',
      options: {
        // 壓縮 jpeg 的配置
        mozjpeg: {
          progressive: true,
          quality: 65
        },
        // 使用 imagemin**-optipng 壓縮 png,enable: false 爲關閉
        optipng: {
          enabled: false,
        },
        // // 使用 imagemin-pngquant 壓縮 png
        pngquant: {
          quality: [0.65, 0.90],
          speed: 4
        },
        // 壓縮 gif 的配置
        gifsicle: {
          interlaced: false,
        },
        // 開啓 webp,會把 jpg 和 png 圖片壓縮爲 webp 格式
        webp: {
          quality: 75
        }
      }
    }
  ]
}
複製代碼

gZip 加速優化

全部現代瀏覽器都支持 gzip 壓縮,啓用 gzip 壓縮可大幅縮減傳輸資源大小,從而縮短資源下載時間,減小首次白屏時間,提高用戶體驗。

gzip 對基於文本格式文件的壓縮效果最好(如:CSS、JavaScript 和 HTML),在壓縮較大文件時每每可實現高達 70-90% 的壓縮率,對已經壓縮過的資源(如:圖片)進行 gzip 壓縮處理,效果很很差。

const CompressionPlugin = require('compression-webpack-plugin')
configureWebpack: (config) => {
  if (process.env.NODE_ENV === 'production') {
    config.plugins.push(
      new CompressionPlugin({
        // gzip壓縮配置
        test: /\.js$|\.html$|\.css/, // 匹配文件名
        threshold: 10240, // 對超過10kb的數據進行壓縮
        deleteOriginalAssets: false, // 是否刪除原文件
      })
    )
  }
}
複製代碼

tree-shaking(求助,生產環境打包生成不了樣式)

按以上的方式構建項目,在開發環境一直都是順順利利的,然而一執行 npm run build,打開頁面,發現樣式全都缺失了。打開 dist/css 文件夾,發現三個 css 文件,只有 index.css 有部分文件(是 main.js 引入額初始化樣式,但也是不全的),另外兩個 css 文件則是空空如也,也就是.vue 裏面的樣式全都缺失了。

查看資源,初步判斷爲 webpack4 默認使用 tree-shaking,會把 在模塊的層面上作到打包後的代碼只包含被引用並被執行的模塊,而不被引用或不被執行的模塊被刪除掉,以起到減包的效果。可是我已經按相關資源在 package.json 配置了 sideEffects 了,可是仍是沒用,實在苦惱!!!

"sideEffects": [
    "*.less",
    "*.css",
    "*.vue"
  ]
複製代碼

在此求助於各位大佬,若有遇過相似問題或者知道解決方法的,請不吝賜教,小弟不勝感激!!!

推薦文章

關注的個人公衆號不按期分享前端知識,與您一塊兒進步!

相關文章
相關標籤/搜索