webpack使用

Webpack是一個現代js應用的模塊打包機。若是一個文件依賴另外一個文件,webpack認爲這就存在一個依賴關係。無論另外一個文件是什麼內容,image,css或js都被看成一個模塊。Webpack從entry points開始構建依賴關係圖,將應用所須要的全部模塊處理成瀏覽器可識別的格式,再打包成一批(個)文件,未來發送給瀏覽器。
chunk:多個模塊打包以後的代碼集合。
css

使用步驟:

1.全局安裝webpackhtml

  npm install -g webpackvue

  若是沒有package.json,使用CLI建立:npm initnode

2.項目根目錄安裝webpackreact

  npm install --save-dev webpackjquery

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

4.運行webpackgit

在項目根目錄經過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.單連字符取代駝峯命名,用於鏈接單詞或指定很短的參數選項(一般只有一個字母)。

github

或在Node中調用web

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)
        }
    ]
相關文章
相關標籤/搜索