webpack在PC項目中的應用

yousuf.wan 8年前發布 | 16K 次閱讀 webpack 前端技術

好東西,總是要使用的。

webpack是什么工具

webpack is a module bundler

正如官網對webpack的描述,它是一種模塊化加載器,當然也不僅僅限于此。某種程度上來說,可以代替某些gulp的功能,至少有些還是無法替代的。在webpack中所有的資源都會被視作模塊來處理,為了應對這樣的情況,webpack有對應的loader機制來處理,另外shim,plugins,和其他構建工具,一樣一樣的,更多的細節,需要你在實際的應用中慢慢去體會了。

webpack的使用方法

安裝:npm install webpack --verbose --save-dev

webpack認為一個項目(或者一個頁面),總有一個入口文件,就像C語言中總有一個main函數一樣。假設,我們創建兩個文件./mian.js./query.js,并且將main.js做為我們項目的入口文件。

query.js

    module.exports = function(){
         var version = 1.0.0;
         console.log(version)
    }

main.js

    var query = require('./query');
    query();

創建一個webpack.config.js文件

    var config = {
        entry:'./main.js',
        ouptut:{
            path:'./js'
            filename:'main.js'
        }
    }
    module.exports = config;

在你的終端上運行webpack即可。

webpack配置詳解

entry

entry屬性做為可配置的入口,比如上面所寫的./main.js。entry有三種寫法,每一個入口可以稱之為一個chunk。

  • 如果為字符串,只會打包一個順序依賴的模塊,輸出則根據output配置而定。
  • 如果為數組,只會打包一個順序依賴的模塊,合并到最后一個模塊時導出,輸出則根據output配置而定。
  • 如果為對象,則會根據入口打包多個順序依賴的模塊,key名會根據在output的配置輸出。

output

輸出規則,在此對象中設置。

  • path 設置輸出的文件路徑
  • filename 設置輸出文件名,filename可以有多種配置,比如main.js[id].js[name].js[hash].js
  • publicPath 設置資源的訪問路徑
  • library 設置模塊導出的類名
  • libraryTarget:'umd' 設置模塊兼容模式
  • umdNamedDefine:true 同上

devtool

將devtool設置為source-map,在開發調試階段非常有用,它的模式非常多,我有搞的比較暈。

loader

loader機制應該是webpack中非常重要的部分了,它是一系列資源的最終執行者。一般情況下,你可以訪問:webpack loader來訪問可用loader列表。

比如現在我想將.html類型的文件,當做一個模塊來載入。

npm install raw-loader
    module:{
        loaders:[
            {
                test:/\.html$/,
                loader:'raw',
                exclude:/(node_modules)/
            }
        ]
    }

每一個loader都可以用一個對象來描述,test是你的匹配規則,loader是你要載入的loader,exclude是你在執行規則是想忽略的目錄。

plugins

webpack的插件機制也非常的重要,其內置了多種插件,比如混淆,壓縮等等。插件列表可以訪問:list of plugins

正常情況下可以使用官方自帶的插件:

    new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        }
    })

當然,我們也可以引入第三方插件,使用你的npm install吧。

resolve

此配置可以對一些常用模塊設置別名,比如a.js放置在./src/module/address/中,每次載入模塊需要var a = require('./src/module/address/a');名字非長,如果設置別名了,只需要var a = require('a');

    resolve:{
        alias:{
            "RequestModel":path.resolve(__dirname,'src/lib/request.model')
        }
    },

還可以設置訪問路徑,以及模塊載入后綴。

    resolve:{
        root:path.resolve(filePath,'/src'),
        extensions:['','.js']
    }

externals

此項配置可以將某些庫設置為外部引用,內部不會打包合并進去。

    externals:{
        jquery:'window.jQuery'
    }

在我們PC項目中的應用

我們公司內部的項目,也開始應用npm scripts來做執行鉤子,webpack來做構建,首先設計三個命令:

  • npm run start
  • npm run dev
  • npm run build

配置npm run start

本地服務器的啟動,我們沒有使用webpack官方提供的webpack-dev-server,而是采用了 browser-sync。

var browser = require('browser-sync');
var browserSync = browser.create();
var PORT = 4000
var loadMap = [
    'modules/*.*',
    'src/**/*.*',
    './*.html',
       './web/*.html'
];
gulp.task('server',[], function() {
    // content
        browserSync.init({
            server:'./',
            port:PORT
        });
        gulp.watch(loadMap, function(file){
            console.log(file.path)
            browserSync.reload()
        });
});

利用gulp寫了一個腳本任務,在package.json文件中設置:

    scripts:{
        "start":"gulp server"
    }

了解我們項目的實際需求

我們的項目是一個多頁面項目,并不像單頁應用一樣(業務編程可以打包成一個),首先我們需要設計一個良好的目錄結構,如下:

  • web 目錄放置*.html頁面
  • style 目錄放置*.css文件,另外在此目錄中放置了less源文件
  • src 目錄放置了我們的所有*.js文件
  • mock 內置的模擬數據,放置在此
  • img 圖片放置目錄
  • link npm下載不了的第三方庫放置在此
  • YYT_PC_Modules 內部編寫的模塊,放置在此
  • YYT_PC_Component 內部編寫的組件,放置在此

編寫一個map.json文件,用來維護多入口的關系。當然我們的webpack.dev.config.js文件,放置在根目錄。最后的build階段應該輸出一個新的目錄dist這個目錄中放置的應該是所有build完成的資源,包括*.html文件。

它應該才是我們最終的發布目錄。

配置npm run dev

對于CSS我們的期望是一個新的link而不是style內嵌,所以還需要做一些額外的事情,先下載loader和插件。

列表:

"less-loader": "^2.2.2",
"raw-loader": "^0.5.1",
"style-loader": "^0.13.0",
"css-loader": "^0.23.1",
"eslint": "^2.2.0",
"eslint-loader": "^1.3.0",
"extract-text-webpack-plugin": "^1.0.1",

raw-loader主要用來解決模板載入的問題,模塊當做一個變量直接載入到業務編程中。

配置我們的CSS:

   // webpack.dev.config.js
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var extractLESS = new ExtractTextPlugin('../style/[name].css');
    {
        test: /\.less$/i,
        loader: extractLESS.extract(['css','less'])
    }

    //入口js文件

    require('../style/less/index.less')

ExtractTextPlugin的作用就是將CSS單獨輸出一個文件,這個文件名依賴于entry寫的入口文件名。

配置我們的多頁面JS:

    //利用了entry的對象寫法
    {
        "index.main":"./src/index.mian.js"
    }

    //然后在輸出是用[name]代替之前的'index.main.js'

提取JS文件中的公共部分:

webpack自帶的一個插件,可以提取合并打包時的公共部分,只要在頁面載入時,放置在合并打包后文件的前面。

new optimize.CommonsChunkPlugin('common.js')

完整的dev構建腳本

var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
var plugins = [];
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var optimize = webpack.optimize
var extractLESS = new ExtractTextPlugin('../style/css/[name].css');
plugins.push(extractLESS);
plugins.push(new optimize.CommonsChunkPlugin('common.js'));
var sourceMap = require('./map.json').source;
var YYT_PC_Modules = 'link/YYT_PC_Modules/';
var YYT_PC_Component = 'link/YYT_PC_Component/';
var config = {
    entry: sourceMap,
    output: {
        path: path.resolve(__dirname + '/js'),
        filename: '[name].js'
    },
    devtool: 'source-map',
    module: {
        loaders: [
            {
                test: /\.html$/,
                loader: 'raw',
                exclude: /(node_modules)/
            },
            {
                test: /\.js$/,
                loader: 'eslint-loader',
                exclude: /(node_modules)/
            },
            {
                test: /\.less$/i,
                loader: extractLESS.extract(['css', 'less'])
            },
            {
                test: /\.(png|jpg)$/,
                loader: 'url-loader?limit=8192'
            }
        ]
    },
    plugins: plugins,
    resolve: {
        alias: {
            "tplEng": path.resolve(__dirname, 'link/template'),  //模板引擎
            "BaseModel": path.resolve(__dirname, YYT_PC_Modules + 'baseModel'),
            "BaseView": path.resolve(__dirname, YYT_PC_Modules + 'baseView'),
            "store": path.resolve(__dirname, YYT_PC_Modules + 'store/locationStore'),
            "cookie": path.resolve(__dirname, YYT_PC_Modules + 'store/cookie'),
            "url": path.resolve(__dirname, YYT_PC_Modules + 'util/url'),
            "tools": path.resolve(__dirname, YYT_PC_Modules + 'util/tools'),
            "FlashAPI": path.resolve(__dirname,YYT_PC_Modules + 'util/FlashAPI'),
            "DateTime": path.resolve(__dirname, YYT_PC_Modules + 'util/DateTime'),
            "pwdencrypt": path.resolve(__dirname, YYT_PC_Modules + 'crypto/pwdencrypt'),
            "secret": path.resolve(__dirname, YYT_PC_Modules + 'crypto/secret'),
            "UploadFile": path.resolve(__dirname, YYT_PC_Component + 'feature/UploadFile'),
            "AjaxForm": path.resolve(__dirname, YYT_PC_Component + 'feature/AjaxForm'),
            "Scrollbar": path.resolve(__dirname, YYT_PC_Component + 'feature/Scrollbar'),
            "LoginBox": path.resolve(__dirname, YYT_PC_Component + 'business/LoginBox/'),
            "UserModel": path.resolve(__dirname, YYT_PC_Component + 'business/UserModel/'),
            "UploadFileDialog": path.resolve(__dirname, YYT_PC_Component + 'business/UploadFileDialog/'),
            "ui.Dialog": path.resolve(__dirname, YYT_PC_Component + 'ui/dialog/'),
            "ui.Confirm": path.resolve(__dirname, YYT_PC_Component + 'ui/confirm/'),
            "ui.MsgBox": path.resolve(__dirname, YYT_PC_Component + 'ui/msgBox/'),
            "config": path.resolve(__dirname, 'src/lib/config')
        }
    },
    externals: {
        jquery: 'window.jQuery',
        backbone: 'window.Backbone',
        underscore: 'window._'
    }
};
// console.log(path.resolve(__dirname,'node_modules/jquery/dist/jquery.js'))
module.exports = config;

問題

問題一:在windows機器上如果你要設置環境變量(也許是我沒有找到問題的所在,但是提出來,主要是Mac用習慣了。)

    scripts:{
        "dev":"WEB_PACK=1 webpack --watch --config webpack.dev.config.js "
    }

在webpack.dev.config.js文件中不能正確的獲取環境變量,所以重新寫了一個build文件,webpack.build.config.js來做最后的構建。

問題二:構建之后的文件hash化后如何更新HTML中的路徑

這個問題,最后沒有采用webpack來做,而是使用gulp來解決的。(不知道大家有沒有什么好的方式)

感謝@sharkrice告知 HtmlWebpackPlugin 插件

問題三:構建系統的shim

我們的PC項目(兼容IE8+),依然使用著以前的庫,jQuery.js,underscore.js,backbone.js,以及其他第三方不支持commonjs語法的插件,有很多兼容不是很好,最后還是寫了另外一個入口文件(包裝),然后在webpack合并打包的文件之前引入這些插件,掛載在一個全局的命名空間下,利用新寫的一個包裝入口導出模塊。

問題四:CSS依賴重復

感謝@sharkrice 告知 嘗試webpack.optimize.DedupePlugin,問題解決。

來源:github.com/icepy/_posts/issues/25

 本文由用戶 yousuf.wan 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!