webpack使用


Webpack是一個現代js應用的模塊打包機。如果一個文件依賴另一個文件,webpack認為這就存在一個依賴關系。不管另一個文件是什么內容,image,css或js都被當作一個模塊。Webpack從entry points開始構建依賴關系圖,將應用所需要的所有模塊處理成瀏覽器可識別的格式,再打包成一批(個)文件,將來發送給瀏覽器。
chunk:多個模塊打包之后的代碼集合。

使用步驟:

1.全局安裝webpack

  npm install -g webpack

  如果沒有package.json,使用CLI創建:npm init

2.項目根目錄安裝webpack

  npm install --save-dev webpack

3.配置webpack.config.js文件,運行webpack命令時不用再輸入參數

4.運行webpack

在項目根目錄通過CLI調用:$webpack   #必須全局安裝,否則需要指定webpack.bat的文件路徑
  webpack默認只會在當前shell工作目錄找webpack.config配置文件;如果配置文件不在shell當前工作目錄,可以通過--config ./file/pathOfConfig 進行傳參數。如果沒有配置文件,則需要輸入webpack <entry> <output>.
  webpack內部自定義了一些參數的映射,運行webpack時可以傳入,如--debug映射為debug:true;-p映射為--optimize-minimize --optimize-occurrence-order.
  單雙連字符的使用沒有特殊規定:Two hyphen-minus characters ( -- ) are used on some programs to specify "long options" where more descriptive option names are used.單連字符取代駝峰命名,用於連接單詞或指定很短的參數選項(通常只有一個字母)。

或在Node中調用

var webpack = require('webpack');  //只需要本地安裝即可 var config2 = require('./webpack.config');
var compiler = webpack(config2);

webpack的配置文件webpack.config.js 

如果該文件存在,只需要執行CLI命令:$webpack,webpack就會自動讀取配置,並輸出打包的文件。配置文件是以js文件的形式代替命令行形式參數,是符合commonjs規范的node模塊,里面可以有function和require(),只要最后以對象形式輸出配置即可。

具體配置信息如下:

devtoolsource-map.生成何種類型的source map,方便打包后的調試。

entry webpack從哪里開始構建整個依賴關系。動態的模塊不能是entry point。一般一個HTML頁面一個entry point。如果有多個entry point,可以采用對象形式定義。不支持通配符配置。如果確實有需要,用如下sinppet,然后將entries傳入webpack.config中,或利用node-glob模塊讀取文件名。

var glob = require("glob");  // Match files using the patterns the shell uses

entry: glob.sync("./src/scripts/*.js")

output打包后輸出的路徑(path)和輸出的文件名(filename)。可以有多個entry point,但只能有一個output。

如果是多entry,filename需要使用替換保證每個輸出文件的名字唯一:

  [name] is replaced by the name of the chunk.

  [hash] is replaced by the hash of the compilation.(compilation對象的hash值,和webpack的compiler環境相關,同一次編譯時的值一樣)

  [chunkhash] is replaced by the hash of the chunk.(具體模塊的hash值,和整個文件內容相關。內容不變,chunkhash值不變)

loaderswebpack將每個資源文件當作一個模塊,但是webpack只能處理js。通過loaders將其他格式的資源文件轉換成模塊后(比如將JSX語言轉換成js、將coffeescript轉換成js)加入依賴關系中,最后打包輸出。需要單獨安裝包並且在webpack.config.js下的modules關鍵字下進行配置loaders后才能使用。 loders下的字段:

  test:確定哪些文件將被加載器處理;

  user:使用哪個加載器,”-loader”后綴不能省略;

  include/exclude:必須處理或屏蔽不需要處理的文件(文件夾)(可選);

  options:當前loader需要的特殊配置(可選),如babel-loader的.babelrc配置文件里的信息。webpack2.5之前為query

plugins擴展Webpack功能,會在整個構建過程中生效,執行相關的任務。需要通過require()引入並將插件實例添加到plugins數組中。

resovle通過別名、擴展名、根路徑或備用目錄等屬性決定webpack如何更快找到import/require()的模塊。

  resolve.alias:用別的路徑或模塊替代。把requirejs項目改為webpack項目時可以利用此屬性。

  resolve.extensions:通過擴展名組成的數組解析require()的模塊文件。比如加載一個coffeeScript文件,需要增加’.coffee’擴展名。若修改后必須增加空字符串為第一個元素。

常用命令:webpack  –watch改動代碼后自動打包 

自動刷新頁面顯示修改后的效果

使用webpack-dev-server模塊構建本地服務器:

  npm install --save-dev webpack-dev-server

安裝完畢之后運行$webpack-dev-server --open

如果報命令不識別的錯誤,可在package.json的script字段添加("start": "webpack-dev-server --progress  --hot --inline --content-base ./dist"),然后運行$npm start命令;

或再全局安裝一次。

壓縮bundle.js文件的大小

//會加載整個lodash,所以體積更大
import { concat, sortBy, map, sample } from 'lodash';
//Use relative file paths,只加載需要的函數模塊,體積更小
//例如import { Button } from 'antd';使用babel-plugin-import插件==》var _button = require('antd/lib/button');
import concat from 'lodash/concat';
import sortBy from 'lodash/sortBy';
import map from 'lodash/map';
import sample from 'lodash/sample';

生產環境中要刪除sourcemap

使用 webpack –p命令構建bundle.js

通過命令webpack --profile --json >> stats.json生成構建過程的文件,在 https://webpack.github.io/analyse上分析哪個模塊占用的空間大。或者使用webpack-bundle-analyzer插件,用視圖的形式分析bundle.js中的每個模塊。

var debug = process.env.NODE_ENV !== 'production'; //是否是開發模式,生產模式時需要去除sourcemap
const webpack = require('webpack'); //訪問內置插件
const path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm

const config = {
    devtool: debug ? 'cheap-eval-source-map' : undefined, //開發模式時生成何種類型的源代碼映射文件,方便調試打包后的代碼
    entry: { //webpack打包的切入點,一般一個頁面的業務代碼定義一個entry point,key為輸出chunk的name
        home: ['./footer.js', './home.js'], //若為[string],則將多個相互獨立的文件及其依賴打包成一個chunk
        about: './about.js',
        contact: './contact.js',
        vendor: ['jquery', 'lodash', 'vue']    // 使用commonschunkplugin抽離的公共模塊
    },
    output: { //輸出配置項
        path: path.resolve(__dirname, 'dist'), //打包后輸出的路徑
        filename: '[name].[chunkhash:8].js', //打包后輸出的文件名,多個entry需要替換
        chunkFilename: '[id].js', //name of non-entry chunk files。瀏覽器按需加載的chunk
        publicPath: 'http://cdnOfCompany.com/' // 供webpack及插件在生產模式下更新內嵌到css、html文件里的cdn路徑
    },
    resolve: {
        extensions: ['', '.js', '.vue'],
        fallback: [path.join(__dirname, '../node_modules')],
        alias: {
            '@': resolve('src'), //別名,js中import時使用,在src和@import中需要加 ~
            'moment': path.join(__dirname, '../node_modules/moment/min/moment-with-locales.min.js'), // 為模塊配置別名
        }
    },
    externals: {
        'jquery': 'jQuery' // 希望通過script標簽引進的依賴,可以require/import,但不打包進bundle。必須先加載,webpack會以module的形式對其引用。
    },
    module: { //    webpack2.0的字段名做了些修改
        rules: [//每條rule定義對應的module如何生成,是loaders的alias
            {
                test: /\.(js|jsx)$/, //一個匹配loaders所處理的文件的拓展名的正則表達式
                exclude: /(node_modules|bower_components)/,
                use: [   //使用到的loader及其配置
                    {
                        loader: 'babel-loader', //使用的加載器名稱,-loader后綴不能省略;也可通過?query設置參數
                        options: { //為當前loader設置的參數,對於babel可以提取出單獨放在.babelrc文件中;
                            presets: ['react', 'es2015'],
                            plugins: [require('react-html-attrs')]
                        }
                    }
                ]
            }, {
                test: /\.css$/,
                exclude: /(node_modules)/, //手動添加必須處理(include)或屏蔽不需要處理的文件(文件夾)
                use: [//使用多個loader處理同一源文件,從右往左順序處理
                    //css-loader將css文件當作一個模塊引入當前模塊中,類名相同樣式也不會沖突;style-loader將當前模塊中的樣式引入頁面的style元素中
                    {
                        loader: 'style-loader'
                    }, {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 1
                        }
                    }, {
                        loader: 'less-loader',
                        options: {
                            noIeCompat: true
                        }
                    }
                ]
            }, {
                // 解決圖片的路徑在發布前后不一致的問題(資源打包前在/src/assert和/static中,打包后全部歸並到/dist/static目錄下)
                //vue-cli中:assert中的資源要經過webpack處理,只能通過相對路徑(視為模塊依賴)訪問;/static中的資源不用經過webpack處理,通過絕對路徑訪問
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,  // 小於8kb的直接轉為base64
                loader: 'url-loader',
                options: {
                    limit: 8192,
                    name: utils.assetsPath('img/[name].[hash:7].[ext]') // 被加載器處理后重寫的url路徑,打包之后圖片也會被復制到dist的該路徑中
                }
            }
        ]
    },
    plugins: debug ? [] : [//添加插件實例
        //依據模板,生成最終的html文件。該文件中會生成標簽自動引用了打包后的js/css文件,不用手動一個一個修改url。
        new htmlWebpackPlugin({     // 一個單頁應用配置一個該實例,多個頁面配置多個
            filename: 'index.html', //輸出文件的名字
            template: './src/index.tpl', //使用的模板文件
            title: 'this is home page', //傳入模版的參數,在模版中可以通過<%=htmlWebpackPlugin.options.title%>獲取該值;也可以傳遞其他自定義的屬性供模板使用,如css等靜態資源,不會被當做依賴被打包。
            favicon: 'path/to/yourfile.ico', //生成的 html 標簽中會包含這樣一個 link 標簽指向favicon
            inject: 'body', //插入script的位置
            chunks: ['vendor', entry],     //不設置時默認包含entry中所有的chunks(包括CommonsChunkPlugin插件生成的chunk)
            excludeChunks: ['contact', 'other chunk']//排除引入的chunk
        }),
        new webpack.optimize.DedupePlugin(),
        new webpack.NoErrorsPlugin(),    //構建過程中發生error時跳過錯誤繼續生成
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.DefinePlugin({      // webpack定義的全局常量,編譯后會替換成value對應的文本,減少手動一個修改 去除對warning和一些提示信息的代碼
            'process.env': {
                'NODE_ENV': JSON.stringify('production')
            }
        }),
        // https://github.com/LiPinghai/UglifyJSDocCN/blob/master/README.md
        new webpack.optimize.UglifyJsPlugin({    //簡化壓縮代碼,去除console,warning等語句
            sourcemap: false,
            compress: {
                screw_ie8: true,
                warnings: false,
                drop_debugger: true, // 去除debugger語句
                pure_funcs: ['console.log'], // 發布時不被打包的函數
                dead_code: true    // 去除不被執行的代碼
            },
            mangle: {
                screw_ie8: true
            },
            output: {
                comments: false,
                screw_ie8: true
            }
        }),
        // http://webpack.github.io/docs/list-of-plugins.html
        // https://stackoverflow.com/questions/39548175/can-someone-explain-webpacks-commonschunkplugin
        new webpack.optimize.CommonsChunkPlugin({   //把所有入口節點的公共代碼提取出來,生成一個chunk(name為common.js)進行復用
            names: ['vendor', 'manifest'],     // 需要抽離的公共模塊的名字。配置manifest,其他代碼改變但vendor不變化時common.js不變,但manifest的hash值改變(因為含有webpack runtime代碼)
            filename: 'commons.js',  // 最后生成公共chunk的文件名,優先於output里的filename配置。不建議固定文件名,如果更新了不方便用戶重新下載。
            minChunks: Infinity // with more entries, this ensures that no other module goes into the vendor chunk
        }),
        new webpack.DllReferencePlugin({    // 引用dll包中的模塊,在dll包中無法找到時才打包進當前bundle
            context: __dirname,
            manifest: require('./manifest.json')
        }),
        // 復制/static目錄中的資源到 dist/static下對應的目錄中
        new CopyWebpackPlugin([
            {
                from: path.resolve(__dirname, '../static'),
                to: config.build.assetsSubDirectory,    // dist/static下
                ignore: []
            }
        ])
    ],
    watch: true, //監聽源文件的修改,之后recompile,但不刷新頁面。為了提高性能,需要將safe write關閉,用save file觸發。
    watchOptions: {
        aggregateTimeout: 300, //從修改文件開始到rebuilding的延遲時間,將多個改變積累到一起
        poll: 1000, //Check for changes every second
        ignored: /node_modules///排除監聽的目錄
    },
    devServer: { //構建本地服務器實時提供服務並刷新,編譯后的文件保存在內存中,所以比較快。
        contentBase: './', //本地服務器所提供服務的內容來源
        port: 8080, //監聽端口,和編輯器的端口不一樣
        colors: true, //終端中輸出結果為彩色
        historyApiFallback: true, //所有的跳轉將指向index.html
        inline: true //會輸出錯誤
    }
};
module.exports = config;
View Code

 code-spitting

/**每個打包后的bundle都有一個webpack啟動函數,負責在客戶端啟動js;使用commonchunkplugin后runtime函數在最后一個chunk中**/ (function(modules) { // webpackBootstrap 

原因:webpack全部打包進一個文件,只要一修改代碼,hash就會改變,客戶端只能重新下載無法利用緩存。另外有些模塊特定的用戶不一定用到,首次訪問時全部加載會影響首屏速度。

方法:

1. 分離業務邏輯,使用CommonsChunkPlugin打包第三方類庫,注意需要在業務邏輯模塊之前下載公共模塊,因為webpackBootstrap函數會在公共模塊中。

<script src="common.js" charset="utf-8"></script>    // webpackBootstrap
<script src="app.js" charset="utf-8"></script>    // webpackJsonp

2. 動態加載指定模塊

import()語法

// other logic
import('./sideAd').then(_ => doSomething()).catch(error => 'An error occurred while loading the component');
require.ensure([dependency],function(require){var footAd = require('./footAd') })
// webpack在靜態打包時,會將sideAd模塊當做單獨的chunk打包,將footAd及其依賴單獨打包(被webpackJsonp函數包裹),這樣當前模塊文件體積就會減小

webpack將import('...')轉變成``__webpack_require__.e/* import() */(n).then()``*或者``__webpack_require__.e/* require.ensure */(0).then((function (require) {__webpack_require__(n)}))``的形式,在瀏覽器中webpackbootstrap會*通過__webpack_require__.e(JSONP方法)動態加載chunkId為n的模塊。*
require():在webpack項目中只能靜態打包到bundle中。依賴都被加載且都被執行后才執行回調函數。
require.ensure():單獨打包,動態加載。依賴被下載后不會被執行,只有在回調函數**再次**使用require(module)后,這個模塊才會被執行。

3. 使用DllPlugin和DllReferencePlugin

將經常使用的庫文件打包到dll包中,它本身不能運行,是用來給業務代碼引用的。分離了第三方庫,打包速度更快,而且不受業務模塊的chunkhash影響。使用步驟:
a. 使用webpack.DllPlugin插件打包第三方庫
b. 在主配置文件中使用webpack.DllReferencePlugin插件引用dll包

4. 對於單頁應用,可以使用bundle-loader異步加載各路由對應的組件(懶加載),盡快顯示首屏。
5. 在vue-router中,使用``const Foo = () => import('./Foo.vue')``懶加載。

    routes: [
        {
            path: '/',
            name: 'Hello',
            component: () => import('../module/HelloWorld')
        },
        {
            path: '/check',
            name: 'check',
            component: r => require(['@/pages/spa/module/CheckBox'], r)
        }
    ]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM