使用webpack搭建vue項目

有一句話叫「前人栽樹後人乘涼」,還有一句話叫「若是說我看得比別人更遠些,那是由於我站在巨人的肩膀上」。前一句是國人的俗語,後一句是那個發現了「萬有引力」定律的牛頓說的。爲何要引用這兩句呢?是由於我剛開始用vue的時候,使用的是vue-cli來搭建vue項目,快速又好用;我剛開始用react的時候,使用的是create-react-app來搭建react項目,方便又省事。使用這些已有的腳手架來搭建項目,無可厚非,對於新手來講,也確實能快速構建,不作置評。css

既然已經有了這些現成的腳手架了,爲何咱們還熱衷於本身來配置webpack來搭建構建項目呢?由於咱們只有瞭解並學會了配置webpack,咱們才能更好地在打包構建項目時將webpack的性能發揮到極致,才能根據自身項目的實際需求,配置有利於項目開發的各類工具、插件,提升咱們的開發效率。好比咱們在打包項目時,能夠分析哪些地方下降了webpack的打包速度,別人打包速度須要花去十多秒、二十多秒,而你能將打包的速度提高至幾秒,這就是你的優點。固然,涉及到webpack的運行原理以及開發本身的loader或plugin就能夠自行去學習了哈,本文只帶你配置一個webpack來搭建一個vue項目。html

wepack做爲一個「模塊打包機」實際上是依賴了龐大的插件體系,插件體系是webpack的核心,能夠說,webpack的生態就是創建在衆多插件之上的,而開發環境和生產打包環境依賴的插件仍是有所不一樣的,先以開發環境爲例webpack.config.js:vue

const path = require('path');
const Webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');

const resolve = (dir) => {
    return path.join(__dirname, '..', dir)
}

const assetsPath = (_path) => {
    return path.join('static', _path)
}

const isEnvProduction = process.env.NODE_ENV == "production", port = 3003;

module.exports = {
    mode: 'development',
    devtool: 'source-map',
    entry: resolve('src'),
    output: {
        path: resolve('dist'),
        filename: isEnvProduction ? assetsPath('js/[name]-[hash].js') : '[name]-[hash].js',
        chunkFilename: isEnvProduction ? assetsPath('js/[name]-[chunkhash:5].min.js') : '[name]-[chunkhash:5].min.js',
        publicPath: '/',
    },
    resolve: {
        extensions: ['*', '.js', '.vue'], //webpack2.x extensions[0]不能爲空   resolve屬性中的extensions數組中用於配置程序能夠自行補全哪些文件後綴
        alias: {
            '@': resolve('src'),
            // 'vue$': 'vue/dist/vue.esm.js'
        },
    },
    //提取公共代碼
    optimization: {
        splitChunks: {
            cacheGroups: {
                commons: {
                    test: /[\\/]node_modules[\\/]/,    //表示默認拆分node_modules中的模塊
                    name: "vendor",  //提取出來的文件命名
                    chunks: "all",     //提取全部文件的公共部分
                    minChunks: 2,         //表示提取公共部分最少的文件數  模塊被引用>=2次,拆分至vendors公共模塊
                    minSize: 0,         //表示提取公共部分最小的大小  模塊超過0k自動被抽離成公共模塊
                },
            }
        }
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: ['vue-loader'],
                exclude: /node_modules/,
            },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                query: {
                    "presets": ["@babel/env"],
                    "plugins":
                        ["@babel/plugin-syntax-dynamic-import", "@babel/plugin-transform-runtime"],
                }
            },
            {
                test: /\.(sa|sc|c)ss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader',
                    'sass-loader',
                ],
            },
            {
                test: /\.(eot?.+|svg?.+|ttf?.+|otf?.+|woff?.+|woff2?.+)$/,
                use: 'file-loader?name=' + (isEnvProduction ? assetsPath('fonts/[name].[hash:8].[ext]') : 'fonts/[name].[hash:8].[ext]')
            },
            {
                test: /\.(jpg|jpeg|png|gif|ico|svg)$/,
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    name: isEnvProduction ? assetsPath('images/[name].[hash:8].[ext]') : 'images/[name].[hash:8].[ext]',
                }
            },
        ],
    },
    plugins: [
        new ProgressBarPlugin(),
        new VueLoaderPlugin(),
        //ProvidePlugin是webpack的內置模塊,使用ProvidePlugin加載的模塊在使用時將再也不須要import和require進行引入
        new Webpack.ProvidePlugin({
            _: 'lodash',
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html',    //文件路徑及名稱
            filename: 'index.html',          //輸出後的文件名稱
        }),
        new MiniCssExtractPlugin({
            filename: isEnvProduction ? assetsPath("css/[name]-[hash].css") : "css/[name]-[hash].css",
            chunkFilename: isEnvProduction ? assetsPath("css/[name]-[hash].css") : "css/[name]-[hash].css",   //默認就是取的以id或name爲開頭的css,因此能夠加這行配置代碼,也能夠不加
        }),
    ],
    devServer: {
        port,
        host: '0.0.0.0',
        open: `http://localhost:${port}`,
        stats: {
            hash: false,
            builtAt: false,
            version: false,
            modules: false,
            children: false,   ////解決相似Entrypoint undefined = index.html和Entrypoint mini-css-extract-plugin = *的警告
            entrypoints: false,
            colors: {
                green: '\u001b[32m',
                yellow: '\u001b[32m',
            }
        },
        proxy: {
            '/': {
                target: '',
                changeOrigin: true
            }
        },
        inline: true,
        compress: false,
        disableHostCheck: true,
        historyApiFallback: true,
    },
}

關於配置中用到的一些插件的api就不一一展開詳解了,惟一須要說明的一點是,配置中所用到的插件的版本基本都是最新的,而使用postcss-loader時,須要在項目的根目錄新建一個postcss.config.js文件:node

module.exports = {  
  plugins: [require('autoprefixer')()]
}

以上是開發環境的webpack配置,下邊是打包生產環境的配置webpack.product.config.js:react

const path = require('path');
const config = require('./webpack.config');
const merge = require('webpack-merge');
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");  //壓縮單獨的css文件
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');  //資源清單
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");   //監控打包文件所花費的時間,方便具體的性能優化
const smp = new SpeedMeasurePlugin();
const PurifyCSSPlugin = require("purifycss-webpack");   //css tree-shaking 依賴插件glob-all和purify-css
const glob = require("glob-all");

module.exports = smp.wrap(merge(config, {
  mode: 'production',
  stats: config.devServer.stats,
  devtool: false,
  //當咱們想在項目中require一些其餘的類庫或者API,而又不想讓這些類庫的源碼被構建到運行時文件中,這在實際開發中頗有必要。此時咱們就能夠經過配置externals參數來解決這個問題
  externals: {
    'vue': 'Vue',
    'vuex': 'Vuex',
    'moment': 'moment',
    'vue-router': 'VueRouter',
    'element-ui': 'ELEMENT',
    'ant-design-vue': 'antd',   //使用externals html裏需手動引入一下js,特別注意:還需額外引入moment.js,並放在antd以前,不然會報錯
    'lodash': '_',
  },
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        parallel: true,    //使用多線程並行運行來提升構建速度,默認併發運行數量:os.cpus().length - 1
        uglifyOptions: {
          compress: {
            inline: false,
            drop_console: true,   //是否屏蔽掉控制檯輸出
          },
        }
      }),
      new OptimizeCSSAssetsPlugin()   //壓縮css
    ]
  },
  plugins: [
    new ManifestPlugin(),
    new CleanWebpackPlugin(),
    new PurifyCSSPlugin({
      paths: glob.sync([
        // 要作CSS Tree Shaking的路徑文件
        path.resolve(__dirname, "../src/*.vue")
      ])
    }),
    new HtmlWebpackPlugin({
      template: './src/index.prod.html',    //打包時須要的文件路徑和名稱
      filename: 'index.html',               //打包輸出後的文件名稱
      minify: {   //壓縮html
        removeComments: true,   //刪除註釋
        collapseWhitespace: true   //刪除空格
      }
    }),
  ],
}));

打包的配置中有幾點須要注意:
一、配置中有一個speed-measure-webpack-plugin的插件,能夠監控打包文件所花費的時間,方便具體的性能優化;
二、配置中加入了webpack-manifest-plugin生成資源清單的插件,這個插件所生成的資源清單對服務端渲染SSR很是有用,服務端能夠根據當前的manifest,引入css和js文件;
三、配置中引入了purifycss-webpackglob-all兩個插件並依賴一個purify-css插件用來對css的tree-shaking。shake有搖動、抖動之意,言外之意就是經過抖動將項目中沒有使用卻定義了的js方法給刪除,下降打包後項目的體積,很形象哈。自webpack2開始,webpack就自帶了js的tree-shaking,卻沒有css的tree-shaking,因此咱們就藉助了插件來實現tree-shaking。
四、爲了提升打包的速度以及下降打包後的項目體積,咱們能夠將項目中用到框架採用CDN的方式引入,從而將這部分框架排除在打包以外,而new HtmlWebpackPlugin配置項中的template的路徑引用的index.prod.html文件就是採用CDN的方式引入的第三方的框架,區分了開發環境中的index.html。提高構建速度也能夠經過DllPlugin和DLLReferencePlugin插件來實現,具體配置可參考:http://www.javashuo.com/article/p-exassaep-bd.htmlwebpack

vue的項目目錄:
web

react項目的webpack配置跟vue項目的webpack配置大同小異,這裏再也不多說,最後奉上package.json:vue-router

{
  "name": "webpackvue",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "cross-env BABEL_ENV=development webpack-dev-server --config config/webpack.config.js",
    "build": "cross-env NODE_ENV=production webpack --config config/webpack.product.config.js"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.4.4",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/plugin-transform-runtime": "^7.4.4",
    "@babel/preset-env": "^7.4.4",
    "@babel/runtime": "^7.4.4",
    "autoprefixer": "^9.5.1",
    "babel-loader": "^8.0.6",
    "babel-plugin-import": "^1.11.2",
    "clean-webpack-plugin": "^2.0.2",
    "cross-env": "^5.2.0",
    "css-loader": "^2.1.1",
    "file-loader": "^3.0.1",
    "glob-all": "^3.1.0",
    "html-webpack-plugin": "^3.2.0",
    "lodash": "^4.17.11",
    "mini-css-extract-plugin": "^0.6.0",
    "node-sass": "^4.12.0",
    "optimize-css-assets-webpack-plugin": "^5.0.1",
    "postcss-loader": "^3.0.0",
    "progress-bar-webpack-plugin": "^1.12.1",
    "purify-css": "^1.2.5",
    "purifycss-webpack": "^0.7.0",
    "sass-loader": "^7.1.0",
    "speed-measure-webpack-plugin": "^1.3.1",
    "style-loader": "^0.23.1",
    "uglifyjs-webpack-plugin": "^2.1.3",
    "url-loader": "^1.1.2",
    "vue-loader": "^15.7.0",
    "vue-template-compiler": "^2.6.10",
    "webpack": "^4.31.0",
    "webpack-cli": "^3.3.2",
    "webpack-dev-server": "^3.3.1",
    "webpack-manifest-plugin": "^2.0.4",
    "webpack-merge": "^4.2.1"
  },
  "dependencies": {
    "ant-design-vue": "^1.3.9",
    "element-ui": "^2.8.2",
    "moment": "^2.24.0",
    "vue": "^2.6.10",
    "vue-router": "^3.0.6",
    "vuex": "^3.1.1"
  }
}
相關文章
相關標籤/搜索