沒了腳手架,怎麼用webpack!?

1. 什麼是webpack?

webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(static module bundler),簡單來講,是一個前端模塊化打包編譯工具,基本的功能包括代碼轉換,文件優化,代碼分割,自動刷新等。  
複製代碼

2. 如何運用webpack

webpack配置文件是基於node.js,而且遵循common.js來開發,
一般咱們會在根目錄下新建一個webpack.config.js文件,基本的結構目錄以下圖所示:
複製代碼

2.1 配置

初始化:webpack3中,webpack和cli是在同一個包中,webpack4中已經分開。 yarn add webpack webpack-clicss

  • entry 入口
entry: {
    main: './src/index.js' //單入口
}
複製代碼
entry: {
        main: './src/index.js', //多入口
        test: './src/test.js'
    }, //提示webpack把哪一個文件做爲構建文件的入口
output: {
    path: path.resolve(__dirname,'dist'),//必須是絕對路徑
    //filename: 'bundle.js'
    filename:'[name].[hash:8].js'//打包多個文件,各自名字,而且加上md5戳,8位
}, //構建結束輸出的位置
複製代碼

多入口打包出js。

打包後的js文件,名字bundle.js,若是想運行這個js,可是不能每次打包後都本身手動添加一個html,所以須要一個自動打包html的插件,而且可以自動把打包後的js添加上去。 第一個webpack插件 html-webpack-plugin 打包html做用 單入口html

let HtmlWebpackPlugin = rquire('html-webpack-plugin') //類
plugins: [
    new HtmlWebpackPlugin({
        fileName: 'index.html',//默認就是index.html
        template: './public/index.html',
        //以public下的index.html爲模板打包
        minify: {
            removeAttributeQuotes: true, //去除引號
            removeEmptyAttributes: true //清除全部的空屬性
            ...
        },
        hash: true, //html引js避免緩存
        ...
    })
]
複製代碼

多入口自動打包引入js,利用循環前端

plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html',
            filename: 'index.html',
            minify: {
                removeAttributeQuotes: true,
                removeEmptyAttributes: true
            },
            chunks: ['main'],//指定入口文件名字,自動引入相應js文件
            hash: true
        }),
        new HtmlWebpackPlugin({
            template: './public/index.html',
            filename: 'test.html',
            chunks: ['test'],
            hash: true
        })
    ]
複製代碼

html-webpack-plugin 參數連接vue

每次更改完代碼,不能每次都手動打包,所以須要創建本地開發服務器,插件webpack-dev-server,而且它是在內存中打包,並不會看到dist目錄,和build不一樣;而且修改代碼會實時刷新。java

devServer: {//開發服務器配置,這裏單獨列出,不在plugins內
        before(app){//若是不用轉發,dev-server開啓以前,前端本身mock數據
            app.get('/api/user',function(req,res){
                res.json({name:"fight"})
            })
        },
        contentBase: path.join(__dirname, 'dist'),//來自dist目錄的文件提供服務,默認是output輸出位置
        port: '3000',//更改端口號
        progress: true,//打包時候進度條
        compress:true //啓動gzip壓縮
        <!--proxy: {//node代理,假如頁面訪問api,node自動轉發到3000端口-->
        <!--    '/api':{-->
        <!-- target: 'http://localhost:3000',--> <!-- pathRewrite: {--> <!-- '/api': ''//假如路徑中有/api,重寫置空--> <!-- } --> <!-- }--> <!--}--> }, 複製代碼

2.1.1 編譯css

loader有三種寫法,字符串,數組和對象 當js須要樣式的時候,import並不能解析,所以須要loader來轉換模塊node

  1. yarn add css-loader style-loader
module: {
        rules: [
            //css-loader 解析文中@import,style-loader 再以style方式插入樣式
            //less 下載less less-loader,scss 下載node-sass sass-loader
            {
                test: /\.(css|less)$/,
                use: ['style-loader','css-loader','less-loader']//順序不能變,不然報錯
            }
        ]
    },
複製代碼

2. 除了以style方式插入樣式,還能夠以Link方式插入樣式,這裏須要用到插件 mini-css-extract-plugin 假如css一些寫法好比transform須要兼容其餘瀏覽器,須要 postcss-loader 和插件 autoprefixer,而且根目錄下新建postcss.config.js,內容以下

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

複製代碼
let MiniCssExtractPlugin = require(mini-css-extract-plugin)
{   
    test: /\.(css|less)$/,
    use: [
        MiniCssExtractPlugin.loader, //抽取樣式到Link標籤中 mini-css-extract-plugin 
        {
            loader: 'css-loader',
            options: {}
        },
        'less-loader',
        'postcss-loader' //兼容前綴
    ]
}
plugins: [
    new MiniCssExtractPlugin({
        filename: 'main.css' //href名字,以下圖所示
    })
    ] 

複製代碼

2.1.2 編譯js

目標:把ES6語法或者更高級別語法編譯成ES5語法react

  1. @babel/preset-env 預設是插件的集合,能夠在plugins裏配置,或者新建.babelrc文件;
  2. 匹配全部js文件,用babel轉化 @babel/core babel核心包, babel-loader,babel加載器 @babel/preset-env預設轉化語法
  3. 高級語法須要其餘插件補充 @babel/plugin-proposal-decorators@babel/plugin-proposal-class-properties, @babel/plugin-transform-runtime, @babel-polyfill, @babel-runtime
{   
    test: /\.js$/,
    exclude: 'node_modules',
    use: 
    [
        {
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env'],
                "plugins": [
                    ['@babel/plugin-proposal-decorators',{"legacy":true}],//解析裝飾器
                    ['@babel/plugin-proposal-class-properties',{"loose":true}],
                    [ "@babel/plugin-transform-runtime"] //該插件依賴安裝@babel/runtime,解析高級語法,好比yiled
                ]
            }
        },
        'eslint-loader' //根目錄新建.eslintrc.json 配置代碼規則 add eslint eslint-loader -D
    ],
    exclude: /node_modules/
}
複製代碼

yarn add babel-polyfill 解析實例上的高級語法,在index中import進去 若是引入jquery,全局引用的話,暴露$,module中配置expose-loaderjquery

{
    test: require.resolve('jquery'),
    use: {
        loader: 'expose-loader?$',
    }
}

複製代碼

2.1.3 編譯圖片

引入圖片有三種方式: 1:new Image() 2.css background:url() 3.html中建立 yarn add file-loader html-withimg-loader -Dwebpack

module: {
        rules: [
            {
                test: /\.html$/,
                use: 'html-withimg-loader' //解決html中引入img不識別圖片的問題
            },
            {
              test:/\.(png|jpg|gif)$/,
              use: 'file-loader' //會自動建立一個MD5戳的名字,而後把圖片拷貝到打包後的dist 
            }
        ]
    }

複製代碼

可是圖片過多會引發資源請求過多,所以url-loader會優化這個問題 yarn add url-loader -Dgit

{
  test:/\.(png|jpg|gif)$/,
  //url-loader會調用file-loader,優化:避免過多發請求,把小的圖片或icon轉換成base64,體積變大,不適用大圖片
  //use: 'file-loader' //會自動建立一個MD5戳的名字,而後把圖片拷貝到dist 
  use: {
      loader: 'url-loader',
      options: {
        limit: 8*1024 //小於8k的圖片會轉換成base64,避免請求 
      }
  } 
}
複製代碼

2.1.4 實時監控

output:{},
 watch:true,//實時監控
 watchOptions:{
    poll:1000,
    aggregateTimeout: 2000,//若是2秒內沒動做,自動打包, 防抖
    ignored:/node_modules/
},
module:{}

複製代碼

2.1.5 resolve

resolve: {
    modules: [path.resolve('node_modules')],//只查找當前路徑的node_modules
    extensions: ['.js','.less','.json','.css'],//引入文件時省略擴展名
    alias: {//別名
        componets: './src/components/'
    }
}

複製代碼

2.1.6 插件

1.每次打包後都會生成新的dist目錄,clean-webpack-plugin 自動清空打包後的目錄

let CleanWebpackPlugin = require('clean-webpack-plugin')
 plugins: [
    new CleanWebpackPlugin('./dist'),
]
複製代碼
  1. 全局定義環境變量,webpack自帶插件,js中PRODUCTION就是dev
let webpack = require('webpack')
plugins:[
    new Webpack.DefinePlugin({ //定義環境變量
        PRODUCTION:JSON.stringify('dev') // 定義PRODUCTION 是 dev環境
    })
    new Webpack.BannerPlugin('make by jeffywin'),//版權聲明,自帶插件
]
複製代碼

2.1.7 webpack優化

當mode爲production的時候,默認css是不壓縮的,須要插件,直接在module.export中配置optimization

let UglifyJsPlugin = require('uglifyjs-webpack-plugin')
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
optimization: {//優化
    minimizer: [//mode必須是production纔有做用
        new UglifyJsPlugin({
            cache: true,
            parallel: true //並行,加速壓縮
        }),
        new OptimizeCssAssetsPlugin({})//壓縮css
    ]
}

複製代碼

2.1.8 webpack優化 DllPlugin DLLReferencePlugin 動態連接庫

在一般的打包過程當中,你所引用的好比bootstrap、react、react-router、antd、vue、vue-router、vuex 等等衆多庫每次開發打包都會打包進dist目錄中,隨着文件愈來愈大,打包的時間和代碼體積也愈來愈大,因爲這些庫的內容基本不會發生改變,每次打包加入它們無疑是一種巨大的性能浪費。所以DllPlugin的出現就是解決這一問題,用某種方法實現了拆分 bundles,同時還大大提高了構建的速度。

  1. 額外建立一個js文件,DllPlugin插件會生成一個名爲mainfest.json文件,這個文件是用來讓DllReferencePlugin 映射到相關依賴上去
let path = require('path')
let webpack = require('webpack')
module.exports = {
    mode: 'development',
    entry: {
        react: ['react','react-dom',...] 
    },
    output: {
        filename: '_dll_[name].js', // 打包後的名字
        path: path.resolve(__dirname,'dist'),
        libraryTarget: 'commonjs2',
        libraryTarget: 'var',
        library: '_dll_[name]'//打包後會自動添加var _dll_[name] = {},來拿到內部的react,暴露出 (也叫作放入全局域) dll 函數
    },
    plugins:[
        new webpack.DllPlugin({//聲明動態連接庫
            name: '_dll_[name]', //暴露出的 DLL 的函數名
            path: path.resolve(__dirname,'dist','mainfest.json')//manifest json 文件的絕對路徑 (輸出文件)
        })
    ]
}
複製代碼

運行 yarn run build -- --config webpack.dll.js,會生成兩個文件

  1. DllReferencePlugin是在webpack主配置文件中設置
new ReferencePlugin({//開發優化 減小打包體積
        manifest:path.resolve(__dirname,'dist','mainfest.json') //html中還須要全局引用
    })
複製代碼

manifest包含 content 和 name 的對象,或者在編譯時(compilation)的一個用於加載的 JSON manifest 絕對路徑,也就是引用DllPlugin打包後的文件

按需打包前

按需打包後
只是提早打包react和reactDom的狀況下,打包文件大小和時間大大縮短,若是按需加載更多,效率會更高。

總結: webpack.dll.js:DllPlugin 打包生成打包後的文件 和 mainfest.json關係映射文件
webpack.config.js: 主配置文件DllReferencePlugin經過mainfest映射到json文件,json文件調用提早打包好的緩存文件

2.1.9 webpack優化 happypack

當項目比較大時,經過happypack插件,它將任務分配給多個子進程去併發執行,子進程處理完再發給主進程,從而減小構建時間,可是項目比較小時,不建議使用,子進程分配時間反而可能增長構建時間。

使用:經過把原來module中的配置移到plugins中

let Happypack = require('happypack')
module: {
    rules: [
        {
            test: /\.js$/,
            use: 'happypack/loader?id=js',
            exclude: /node_modules/,
            include: path.resolve('src')
        }
    ]
}
plugins: [
new Happypack(
    {
        id: "js",
        use:[{
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env', '@babel/preset-react'],
                "plugins": [
                    ['@babel/plugin-proposal-decorators', { "legacy": true }],//解析裝飾器
                    ['@babel/plugin-proposal-class-properties', { "loose": true }],
                    ['@babel/plugin-transform-runtime'] //該插件依賴安裝@babel/runtime,解析高級語法,好比yiled

                ] //es7高級語法,順序不能變 
            }
        }],
        //'eslint-loader' //根目錄新建.eslintrc.json 配置代碼規則
        }
    )
]
複製代碼

繼續優化 yarn add webpack webpack-cli html-webpack-plugin @babel/core babel-loader @babel/preset-env @babel/preset-react

假如項目中引用了jquery等,能夠用noParse來不去解析有關的依賴項 還有excludes,includes

module: {
        noParse: /jquery/,//不去解析jquery中的依賴項
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,//排除
                include: path.resolve(src),//包含
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            '@babel/preset-env',
                            '@babel/preset-react'
                        ]
                    }
                }
            }
        ]
    },
複製代碼

假如引用了moment時間插件,會自動下載本地語言文件,致使打包文件過大,能夠用webpack自帶插件 webpack.IgnorePlugin

plugins: [
        //若是引入了monent,去除掉本地的語言插件,若是須要能夠單獨引用
        new webpack.IgnorePlugin(/\.\/local/,/moment/),
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: './public/index.html'
        })
    ]
複製代碼

配置以後

多頁面模塊打包抽取公共代碼和第三方庫 假如同時引入了兩個文件,或者jquery等第三方庫,而且是多頁面打包,能夠將公共部分提早抽取出來打包

optimization: {
        splitChunks: {
            cacheGroups: {
                common:{
                    chunks: 'initial',
                    minSize: 0, //超過0個字節
                    minChunks: 2,//用過2次以上就抽離出來
                 },
                 venor: {//第三方
                    priority: 1,//權重,優先抽離
                    test: /node_modules/,
                    chunks: 'initial',
                    minSize: 0, //超過0個字節
                    minChunks: 2,//用過2次以上就抽離出來
                }
            }
        },
    },

複製代碼

Webpack本質是一種事件流機制,工做流程就是將各個插件串連起來,實現這一的核心就是Tapable

相關文章
相關標籤/搜索