webpack學習筆記

命令使用

npm install webpack -g
    做爲全局安裝, 在任意目錄使用

npm install webpack --save-dev
    做爲項目依賴安裝

npm init
    建立package.json

npm install webpack-dev-server --save-dev
    使用webpack-dev-server啓動服務器

webpack --progress -colors
    讓編譯的輸出內容帶有進度和顏色

webpack --watch
    若是不想每次修改模塊後都從新編譯, 那麼能夠啓動監聽模式。
    開啓監聽模式後, 沒有變化的模塊會在編譯後緩存到內存中,
    而不會每次都被從新編譯, 因此監聽模式的總體速度是很快的

webpack --display-error-details
    用來打印錯誤詳情

npm install xxx-loader --save-dev
    安裝多個加載器: npm install babel-core babel-preset-es2015 babel-preset-react

npm webpack --config webpack.config.js
    執行打包命令

npm start
    啓動開發模式下的server

npm run start:prod
    啓動生產模式的server

npm run build
    打包生產模式的代碼

npm run lint: eslint
    代碼檢查

npm run lint:watch: eslint
    監視

npm run remove:build
    刪除dist目錄

npm run clean:build
    清除dist目錄

// 調用webpack
webpack
    開發環境下編譯

webpack -p
    產品編譯及壓縮

webpack --watch
    開發環境下持續的監聽文件變更來進行編譯

webpack -d
    引入source maps

配置文件

webpack.config.dev.js: 開發模式相關配置
webpack.config.prod.js: 生產模式相關配置
server.js: 配置本地的server(包含dev server和prod server) 將server部分分離到一個單獨到的文件配置
package.json
//webpack.config.dev.js

var webpack = require('webpack');
var path = require('path');
var config = {
    // 入口文件配置
    entry: {
        path.resolve(__dirname, 'app/index.js');
    },
    // 文件輸出配置
    output: {
        path: path.resolve(_dirname, 'build'),
        filename: 'bundle.js',
        publicPath: '/'
    },

    // 插件項
    plugins: [],

    // 加載器配置
    module: {
        loaders: [
            {
                test: /pattern/,
                loader: 'xxx-loader',
                exclude: /dir/,
                query: {
                    presets: ['react']
                }
            },
             {
                 test: /\.(png|jpg)$/,
                 loader: 'url-loader?limit=8192'
                 // 內聯的base64的圖片地址, 圖片要小於8k, 直接的url的地址則不解析
             }
        ]
    },
    // 其餘解決方案配置
    resolve: {
        extensions: ['', '.js', '.json'],
        alias: {}
    },
    watch: true
};

module.exports = config;
webpack.server.js

var webpack = require('webpack');
var webpackServer = require('webpack-dev-server');
var config = require('./webpack.config.dev.js');

var compiler = webpack(config);
var server = new webpackDevServer(compiler, {
    contentBase: './app',
    historyApiFallback: true,
    hot: true,        //熱啓動
    inline: true, // 監控js變化
    stats: {
        color: true
    }
});

config.entry.unshift('webpack-dev-server/client?http://localhost:8080/',
    'webpack/hot/dev-server');

server.listen(8080, 'localhost', function(err) {
    if(err) {
        console.log(err);
    }
    console.log('Listening at localhost:8080...');
});

<!-- package.json -->
'script': {
    'start': 'node server.js'
}

配置詳解

entry: 入口, 定義要打包的文件

output: 出口, 定義打包輸出的文件;包括路徑, 文件名,還可能有運行時的訪問路徑(publicPath)參數

module: webpack將全部的資源都看作是模塊, 而模塊就須要加載器;
|---- loaders: 主要定義一些loaders, 定義哪些後綴名的文件應該用哪些loader

|-------- test: 匹配文件後綴, 檢測哪些文件須要此loader, 是一個正則表達式

|-------- exclude: 忽略哪些文件

|-------- query: 參數 (或直接寫於loader如: loader: 'url-loader?limit=8192')

|------------ presets:

resolve: 其餘解決方案配置

|---- extensions: 忽略文件擴展名, require文件時可直接使用require('file'),而非帶後綴如require('file.js')

|-------- alias: 模塊別名定義,方便後續直接飲用別名無需多寫長地址, 後續直接require(key)

plugins: 定義一些額外的插件

watch: 值爲boolean, 監聽文件變化

配置生產環境

開發環境:
    webpack.config.dev.js
    須要日誌輸出, sourcemap, 錯誤報告等

生產環境:
    webpack.config.prod.js
    須要作代碼壓縮, 對文件名進行hash處理等

區分環境

使用DefinePlugin設置環境變量, 根據設置的環境變量決定是否打包壓縮及啓動dev server或prod servercss

plugins: [
    new webpack.DefinePlugin({
        'process.evn.NODE_ENV': JSON.stringify('production')
    });
]

判斷當前是不是生產環境html

var isProduction = function() {
    return process.env.NODE_ENV === 'production';
}


output: {
    path: path.resolve(isProduction ? '__build' : './assets/'),
    filename: isProduction ? '[name].js' : './assets/js/[chunkhash:8].[name].min.js',
    chunkFilename: isProduction ? '[chunkhash:8].chunk.js' : './assets/js/[chunkhash:8].chunk.min.js',
    publicPath: isProduction ? '/__build/' : 'http://cdn.site.com/'
}

代碼壓縮

new webpack.optimizeUglifyJsPlugin({
    compress: {
        warnings: false
    }
});

添加Hash緩存

對於沒有修改的文件, 從緩存中獲取文件, 對於已經修改的文件, 不要從緩存中獲取node

output: {
    //chunkhash 默認16位, 可自定義配置
    filename: '[chunkhash:8].bundle.js'
}

自動生成頁面

文件名帶上hash值後, 這個值在每次編譯的時候都會發生變化,都須要在 html 文件裏手動修改引用的文件名,這種重複工做很瑣碎且容易出錯, 這裏咱們能夠使用 html-webpack-plugin 來幫咱們自動處理這件事情, 用來簡化建立服務於webpackbundle的HTML文件react

解決方案: 在項目目錄下建一個index.tpl.html做爲鉤子jquery

<!-- index.tpl.html -->
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>My APP</title>
    </head>
    <body>
        <div id="app"></div>
    </body>
    </html>

在webpack.config.dev.js和webpack.config.prod.js添加配置代碼, 便可生成相應的index.htmlwebpack

plugins: [
        new HtmlWebpackPlugin({
            template: 'app/index.tpl.html',
            inject: 'body',
            filename: index.html
        })
    ]

加載器

js處理

babel-loader: 轉換JSX
babel-core: babel核心包
babel-preset-react
babel-preset-es2015
<!-- webpack.config.dev.js -->
<!-- babel-loader配置 -->
loaders:[
    {
        loaders: 'xxx-loader',
        query: {
            resets:['react', 'es2015']
        }
    }
]

css處理

style-loader
css-loader
less-loader

img處理

url-loader
    能夠根據自定義文件大小或者轉化爲 base64 格式的 dataUrl, 或者單獨做爲文件, 也能夠自定義對應的hash 文件名
file-loader
    默認狀況下會根據圖片生成對應的 MD5hash 的文件格式
image-webpack-loader
    提供壓縮圖片的功能

加載babel-loader須要配置query參數web

<!-- webpack.config.dev.js -->
<!-- url-loader配置 -->
loaders:[
    {
        test: /\.(jpe?g|png|gif|svg)$/i,
        loaders: [
            // 當內容size小於8KB時, 會自動轉成base64的方式內嵌進去, 這樣能夠減小一個HTTP的請求
            // 當圖片大於8KB時, 則會在img/下生成壓縮後的圖片, 命名是[hash:8].[name].[ext]的形式
            // hash:8的意思是取圖片內容hashsum值的前8位,
            // 這樣作可以保證引用的是圖片資源的最新修改版本, 保證瀏覽器端可以即時更新
            'url?limit=8192&name=img/[hash:8].[name].[ext]',

            // 圖片壓縮
            'image-webpack'
        ]
    }
]
<!-- webpack.config.dev.js -->
<!-- file-loader配置 -->
loaders:[
    {
        test: /\.(jpe?g|png|gif|svg)$/i,
        loaders: [
            // 生成md5 hash格式的文件名
            'file?hash=sha512&digest=hex&name=[hash].[ext]',
            // 圖片壓縮
            'image-webpack'
        ]
    }
]

插件

<!-- webpack.config.dev.js -->
plugins: [definPlugin, bannerPlugin, uglifyJsPlugin...]

設置環境變量

var definPlugin = new webpack.DefinePlugin({
    "process.env": {
        NODE_ENV: JSON.stringify("production")
    }
    // feature flags: 在開發環境(例如日誌)活內部測試環境(例如沒有發佈的新功能)中使用
    // __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')),
    // __PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false'))
});

給輸出的文件頭部添加註釋信息

var bannerPlugin = new webpack.BannerPlugin('This is test!');

JS混淆

var uglifyJsPlugin = new webpack.optimize.UglifyJsPlugin({
    mangle: {
        // 配置如下列表, 在混淆代碼時, 如下配置的變量, 不會被混淆
        except: ['$super', '$', 'exports', 'require']
    }
});

壓縮JS

var minChunkSizePlugin = new webpack.optimize.MinChunkSizePlugin({
    compress: {
        warnings: false
    }
});

壓縮React

var definPlugin = new webpack.DefinePlugin({
    'process.env': {
        NODE_ENV: JSON.stringify('production')
    }
});

加載jQuery

new webpack.ProvidePlugin({
    $: 'jquery'
});

公共模塊提取

new webpack.optimize.CommonsChunkPlugin({
    name: 'vendors',                     // 將公共模塊提取, 生成名爲`vendors`的chunk
    chunks: ['index','list','about'],     //提取哪些模塊共有的部分
    minChunks: 3                         // 提取至少3個模塊共有的部分
});

單獨使用link標籤加載css並設置路徑

new ExtractTextPlugin('css/[name].css'), // 相對於output配置中的publickPath

自動生成html文件, 模板生成的相關配置, 每一個對於一個頁面的配置, 有幾個寫幾個

new HtmlWebpackPlugin({                 //根據模板插入css/js等生成最終HTML
    favicon: './src/img/favicon.ico',     //favicon路徑, 經過webpack引入同時能夠生成hash值
    filename: './view/index.html',         //生成的html存放路徑, 相對於path
    template: './src/view/index.html',     //html模板路徑
    inject: 'body',                     //js插入的位置, true/'head'/'body'/false
    hash: true,                         //爲靜態資源生成hash值
    chunks: ['vendors', 'index'],        //須要引入的chunk, 不配置就會引入全部頁面的資源
    minify: {                             //壓縮HTML文件
        removeComments: true,             //移除HTML中的註釋
        collapseWhitespace: false         //刪除空白符與換行符
    }
});
new HtmlWebpackPlugin({                 //根據模板插入css/js等生成最終HTML
    favicon: './src/img/favicon.ico',     //favicon路徑, 經過webpack引入同時能夠生成hash值
    filename: './view/list.html',         //生成的html存放路徑, 相對於path
    template: './src/view/list.html',     //html模板路徑
    inject: true,                         //js插入的位置, true/'head'/'body'/false
    hash: true,                         //爲靜態資源生成hash值
    chunks: ['vendors', 'list'],        //須要引入的chunk, 不配置就會引入全部頁面的資源
    minify: {                             //壓縮HTML文件
        removeComments: true,             //移除HTML中的註釋
        collapseWhitespace: false         //刪除空白符與換行符
    }
});

其它插件

new webpack.HotModuleReplacementPlugin()     // 熱加載
HotModuleReplacementPlugin()                 // 代碼熱替換
NoErrorsPlugin()                             // 報錯但不退出webpack進程
OpenBrowserPlugin()                         // 自動打開瀏覽器

webpack使用

分析多個模塊的公用代碼提取並單獨打包

var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
module.exports = {
    entry: {
        page1: './main1.js',
        page2: './main2.js'
    },
    output: {
        path: 'build',
        filename: '[name].js'
    },
    plugins: [
        commonsPlugin
    ]
}

文件引用忽略擴展名配置

若是你但願在require文件時省略文件的擴展名, 只須要在webpack.config.js中添加 resolve.extensions 來配置。正則表達式

// webpack.config.js
module.exports = {
    entry: './main.js',
    output: {
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            { test: /\.coffee$/, loader: 'coffee-loader' },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015', 'react']
                }
            }
        ]
    },
    resolve: {
        // 如今你require文件的時候能夠直接使用require('file'), 不用使用require('file.coffee')
        extensions: ['', '.js', '.json', '.coffee']
    }
};

css樣式和圖片的加載

首先你須要用require()去加載你的靜態資源(named as they would with node's require()):npm

require('./bootstrap.css');
require('./myapp.less');

var img = document.createElement('img');
img.src = require('./glyph.png');

當你require了CSS(less或者其餘)文件, webpack會在頁面中插入一個內聯的<style>, 去引入樣式。當require圖片的時候, bundle文件會包含圖片的url, 並經過require()返回圖片的url。json

可是這須要你在webpack.config.js作相應的配置(這裏仍是使用loaders)

// webpack.config.js
module.exports = {
    entry: './main.js',
    output: {
        path: './build', // 圖片和js會放在這
        publicPath: 'http://mycdn.com/', // 這裏用來生成圖片的地址
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            {
                test: /\.less$/,
                loader: 'style-loader!css-loader!less-loader'
            }, // 用!去鏈式調用loader
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {
                test: /\.(png|jpg)$/,
                loader: 'url-loader?limit=8192'
                // 內聯的base64的圖片地址, 圖片要小於8k, 直接的url的地址則不解析
            }
        ]
    }
};

功能標識(Feature flags)

項目中有些代碼咱們只爲在開發環境(例如日誌)或者是內部測試環境(例如那些沒有發佈的新功能)中使用, 那就須要引入下面這些魔法全局變量(magic globals):

if (__DEV__) {
    console.warn('Extra logging');
}
// ...
if (__PRERELEASE__) {
    showSecretFeature();
}

同時還要在webpack.config.js中配置這些變量, 使得webpack可以識別他們。

// webpack.config.js

// definePlugin 會把定義的string 變量插入到Js代碼中。
var definePlugin = new webpack.DefinePlugin({
    __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')),
    __PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false'))
});

module.exports = {
    entry: './main.js',
    output: {
        filename: 'bundle.js'
    },
    plugins: [definePlugin]
};

配置完成後, 就能夠使用 BUILD_DEV=1 BUILD_PRERELEASE=1 webpack來打包代碼了。
值得注意的是, webpack -p 會刪除全部無做用代碼, 也就是說那些包裹在這些全局變量下的代碼塊都會被刪除, 這樣就能保證這些代碼不會因發佈上線而泄露。

多個入口文件

若是你有兩個頁面:profile和feed。若是你但願用戶訪問profile頁面時不加載feed頁面的代碼, 那就須要生成多個bundles文件:爲每一個頁面建立本身的「main module」(入口文件)。

// webpack.config.js
module.exports = {
    entry: {
        Profile: './profile.js',
        Feed: './feed.js'
    },
    output: {
        path: 'build',
        filename: '[name].js' // name是基於上邊entry中定義的key
    }
};

在profile頁面中插入<script src="build/Profile.js"></script>。feed也同樣。

優化通用代碼

Feed和Profile頁面存在大量通用代碼(好比React、公共的樣式和組件等等)。webpack能夠抽離頁面間公共的代碼, 生成一個公共的bundle文件, 供這兩個頁面緩存使用:

// webpack.config.js

var webpack = require('webpack');

var commonsPlugin =
    new webpack.optimize.CommonsChunkPlugin('common.js'); // 引入插件

module.exports = {
    entry: {
        Profile: './profile.js',
        Feed: './feed.js'
    },
    output: {
        path: 'build',
        filename: '[name].js' // 爲上面entry的key值
    },
    plugins: [commonsPlugin]
};

在上一步引入本身的bundle以前引入<script src="build/common.js"></script>

異步加載

雖然CommonJS是同步加載的, 可是webpack也提供了異步加載的方式。這對於單頁應用中使用的客戶端路由很是有用。當真正路由到了某個頁面的時候, 它的代碼纔會被加載下來。

指定你要異步加載的 拆分點。看下面的例子

if (window.location.pathname === '/feed') {
    showLoadingState();
    require.ensure([], function() { // 這個語法痕奇怪, 可是仍是能夠起做用的
        hideLoadingState();
        require('./feed').show(); // 當這個函數被調用的時候, 此模塊是必定已經被同步加載下來了
    });
} else if (window.location.pathname === '/profile') {
    showLoadingState();
    require.ensure([], function() {
        hideLoadingState();
        require('./profile').show();
    });
}

剩下的事就能夠交給webpack, 它會爲你生成並加載這些額外的 chunk 文件。

webpack 默認會從項目的根目錄下引入這些chunk文件。你也能夠經過 output.publicPath來配置chunk文件的引入路徑

// webpack.config.js
output: {
    path: "/home/proj/public/assets", // webpack的build路徑
    publicPath: "/assets/" // 你require的路徑
}
相關文章
相關標籤/搜索