webpack使用小記

對webpack的運用作一些解釋

如何支持多出口?

在用webpack打包的時候,咱們常常遇到一個問題,那就是因爲path的設置,致使咱們不能分目錄打包咱們想要的文件,這樣的問題怎麼辦呢?
其實這樣的問題很好解決。咱們只須要在entry裏作一些改變便可。css

以咱們的項目爲例。我須要對資源生成一張資源地圖,按照資源地圖來製做入口配置。html

var path = require('path');
function getSourceMap(list) {
    var entry = Object.create(null);
    list.forEach(function (src) {
        // 砍掉./static/前綴和.extname後綴
        // 咱們的項目結構,讀者能夠自行配置,不用拘泥於僞代碼
        entry[src.slice(9, src.lastIndexOf('.'))] = src;
    });
    return entry;
}

生成的資源入口最終變成了前端

entry = {
    'market/js/pages/appIndexPage': './static/market/js/pages/appIndexPage.js',
    'market/js/pages/appOrderPage': './static/market/js/pages/appOrderPage.js'
    ...
}

這時候,只要簡單的將輸入靜態資源的出口規定好,咱們的多出口輸出配置就完成了node

output: {
        path: path.resolve(__dirname, 'www/static-mobile/' + version),
        filename: "[name].js"
    }

最終輸出的文件結構以下webpack

+www
    +static-mobile
        +dev
            +market
               + js
                    +pages
                         appIndexPage.js
                         appOrderPage.js
                         ....

如何將webpackDevServer與thinkjs的服務結合起來

本地構建的過程當中,咱們的webpackDevServer其實只充當着靜態資源服務器的角色。這個時候咱們就不可避免的要訪問thinkjs的服務來支撐每一個頁面的展現。nginx


怎麼辦呢?若是啓用nginx,對於前端來講,又是一坨沒有接觸過的東西,不如用咱們熟悉的東西來作。es6

var d = webpack(options);

var WebpackDevServer = require('webpack-dev-server');

var compiler = webpack(options);
var server = new WebpackDevServer(compiler, {
    contentBase:path.join(__dirname, 'www/'),
    compress: true,
    hot: true,
    open: true,
    stats: { colors: true }
    ,
    proxy: [
        {
            path: '/static\-mobile',
            target: 'http://localhost:3000',
            pathRewrite: {
                '^/static-mobile': '/qietv-mobile'
            }
        },
        {
            path: '*',
            target: 'http://localhost:8361',
            context: function (pathname, req) {
                if (/^\/(static\-mobile|qietv\-mobile)/.test(pathname)) {
                    return false;
                }
                return true;
            },
        }
    ]
});

咱們所處的項目的thinkjs服務端口是8361,其實只要將非靜態文件夾所有轉到thinkjs服務上就行。在網上有不少其餘答案,會告訴你只用跟着他的答案寫就好了,卻沒有告訴你爲何。要搞清楚proxy的詳細配置方法,仍是須要去看webpack-dev-server的文檔和http-proxy-middleware的文檔(由於devServer使用的是http-proxy-middleware)。web

什麼?個人熱更新找不到輸出文件?

同窗們可能會疑惑,我啓動了devServer.hot = true,入口也填入了webpack-dev-server/client,plugin也插入了,依然找不到文件,爲何?這個問題在於publicPath設置的錯誤(publicPath須要和output.publicPath一致)json

var server = new WebpackDevServer(compiler, {
    contentBase:path.join(__dirname, 'www/'),
    compress: true,
    hot: true,
    inline: true,
    // publicPath不填默認是'/',
    // 在這裏不填的話,咱們本來輸出到./www/qietv-mobile/dev/client/js/app.js
    // 訪問的時候就變成了 'http://localhost:3000/client/js/app.js'才訪問獲得.
    // 加上一個訪問path,就能夠成功寫出了。
    // publicPath: "/qietv-mobile/" + version + '/'
}

什麼?我設置了publicPath之後依然沒法熱更新?

在設置完publicPath之後記得在pulgin里加入new webpack.HotModuleReplacementPlugin()。除此以外,你還須要在入口文件裏容許須要的模塊熱更新才行。服務器

// 容許全部子模塊熱更新
if (module.hot) {
    module.hot. accept();
    // 只容許某一個模塊熱更新
    // module.hot. accept('./a');
    // 容許多個指定模塊熱更新
    // module.hot. accept(['./a', './b']);
}

可是開發的時候,咱們並非很想寫這個東西對吧,因此此時咱們能夠藉助插件webpack-module-hot-accept(這個插件是一個loader,用於給通過的js增長一個module.hot.accept(xxx)的包裝)。用法請自行百度該插件

webpack如何和別的觀察系統結合

咱們有這麼一種需求,須要將jade文件觀察(jade並不禁咱們的webpack引入),此時若是jade更新,咱們須要刷新頁面。做爲一個懶人咱們又懶得按下f5去刷新,這時候怎麼辦呢?

var webpack = require('webpack');
var config = require('./webpack.config.js');
var webpackDevServer = require('webpack-dev-server');
var server = webpackDevServer(webpack(config),  {
    // someoptions
});
var fs = require('fs-extra');
// 僞代碼,
watch('/watchedDir', function (event) {
    if (event.filepath.test(/\.jade$/)) {
        fs.copy(event.filepath, targetDist)
           .then(function () {
                server.sockWrite(server.sockets, "content-changed");
          })
    }
})

webpack-dev-server向外暴露了sockWrite接口,用於指揮開發頁面執行一些命令(webpack-dev-server使用sockjs和頁面進行通訊)。當發佈content-changed事件的時候,頁面接收到,會被直接刷新。由於content-changed事件對應的操做以下(入口應加入webpack-dev-server/client):

"content-changed": function() {
    log("info", "[WDS] Content base changed. Reloading...")
    self.location.reload();
},

哪些資源可使用取巧的方式使本地開發更加便捷

若是咱們的頁面上有一些資源並不須要編譯,僅僅只是簡單的移動一下位置,那麼咱們簡單作一個快捷方式過去就好了。。。這樣能夠省卻本地開發複製移動這種無聊的事。。。

var views = glob.sync(path.resolve(p, '**/views'));

views.forEach(function (viewSrc) {
    fs.ensureSymlinkSync(viewSrc, path.resolve(__dirname, 'view', viewSrc.match(/\/([a-zA-Z0-9\-]+)\/views$/)[1]));
});

我跟你說這個只是個符號鏈接,當心你操做文件的時候把文件搞空了。

何時須要用到happypack

實際上,happypack是爲了解決大量計算速度上不來的問題。咱們作文件移動,複製,這些事情的時候基本用不上。那何時用呢?首先咱們要明白happypack究竟是什麼東西。happypack自己管理了一些線程,充分利用cpu來說計算密集的事情時間縮短(事實上他這個說法有誤,他用的是chird_process,使用的是子進程,通訊用的是IPC,固然應該是進程池。可是你們寫這種東西習慣性的取這種名字,那就這樣咯)。咱們在作什麼事情的時候會用到cpu計算呢?在解析es6文本,解析其餘預編譯語言的時候,就會用到cpu進行計算。因此在這種時候,咱們須要上happypack進行構建。
happypack參與構建的姿式參考如下片斷

// 例子是掘金上挖過來的,webpack1的寫法,見諒
var babelQuery = webpackConfig.babel;

// 這個size可使用cpu num + 1進行代替
var happyThreadPool = HappyPack.ThreadPool({ size: 25 });
function createHappyPlugin(id, loaders) {
  console.log('id', id)
  return new HappyPack({
    id: id,
    loaders: loaders,
    threadPool: happyThreadPool,

    // disable happy caching with HAPPY_CACHE=0
    cache: true,

    // make happy more verbose with HAPPY_VERBOSE=1
    verbose: process.env.HAPPY_VERBOSE === '1',
  });
}
webpackConfig.module = {};

webpackConfig.module = {
  loaders: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'happypack/loader?id=js',
    },
    {
      test: /\.jsx$/,
      loader: 'happypack/loader?id=jsx',
    },
    {
      test(filePath) {
        return /\.css$/.test(filePath) && !/\.module\.css$/.test(filePath);
      },
      loader: ExtractTextPlugin.extract('style', 'happypack/loader?id=cssWithoutModules')
    },
    {
      test: /\.module\.css$/,
      loader: ExtractTextPlugin.extract('style', 'happypack/loader?id=cssWithModules')
    },
    {
      test(filePath) {
        return /\.less$/.test(filePath) && !/\.module\.less$/.test(filePath);
      },
      loader: ExtractTextPlugin.extract('style', 'happypack/loader?id=lessWithoutModules')
    },
    {
      test: /\.module\.less$/,
      loader: ExtractTextPlugin.extract('style', 'happypack/loader?id=lessWithModules')
    }
  ],
}
if (!!handleFontAndImg) {
  webpackConfig.module.loaders.concat([
    { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=woff' },
    { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=woff2' },
    { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=ttf' },
    { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=eot' },
    { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=svg' },
    { test: /\.(png|jpg|jpeg|gif)(\?v=\d+\.\d+\.\d+)?$/i, loader: 'happypack/loader?id=img' },
    { test: /\.json$/, loader: 'happypack/loader?id=json' },
    { test: /\.html?$/, loader: 'happypack/loader?id=html' }    
  ])
} else {
  webpackConfig.module.loaders.concat([
    { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&minetype=application/font-woff' },
    { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&minetype=application/font-woff' },
    { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&minetype=application/octet-stream' },
    { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
    { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&minetype=image/svg+xml' },
    { test: /\.(png|jpg|jpeg|gif)(\?v=\d+\.\d+\.\d+)?$/i, loader: 'url?limit=10000' },
    { test: /\.json$/, loader: 'json' },
    { test: /\.html?$/, loader: 'file?name=[name].[ext]' }
  ])
}
webpackConfig.plugins.push(createHappyPlugin('js', ['babel?'+JSON.stringify(babelQuery)]))
webpackConfig.plugins.push(createHappyPlugin('jsx', ['babel?'+JSON.stringify(babelQuery)]))
webpackConfig.plugins.push(createHappyPlugin('cssWithoutModules', ['css?sourceMap&-restructuring!postcss']))
webpackConfig.plugins.push(createHappyPlugin('cssWithModules', ['css?sourceMap&-restructuring&modules&localIdentName=[local]___[hash:base64:5]!postcss']))
webpackConfig.plugins.push(createHappyPlugin('lessWithoutModules', ['css?sourceMap!postcss!less-loader?sourceMap']))
webpackConfig.plugins.push(createHappyPlugin('lessWithModules', ['css?sourceMap&modules&localIdentName=[local]___[hash:base64:5]!postcss!less-loader?sourceMap']))
if (!!handleFontAndImg) {
  webpackConfig.plugins.push(createHappyPlugin('woff', ['url?limit=10000&minetype=application/font-woff']))
  webpackConfig.plugins.push(createHappyPlugin('woff2', ['url?limit=10000&minetype=application/font-woff']))
  webpackConfig.plugins.push(createHappyPlugin('ttf', ['url?limit=10000&minetype=application/octet-stream']))
  webpackConfig.plugins.push(createHappyPlugin('eot', ['file']))
  webpackConfig.plugins.push(createHappyPlugin('svg', ['url?limit=10000&minetype=image/svg+xml']))
  webpackConfig.plugins.push(createHappyPlugin('img', ['url?limit=10000']))
  webpackConfig.plugins.push(createHappyPlugin('json', ['json']))
  webpackConfig.plugins.push(createHappyPlugin('html', ['file?name=[name].[ext]']))
}

本地開發須要全量構建嗎?

本地開發追求的是開發速度,並不須要全量構建,甚至能夠說,咱們只須要指定子目錄運行webpackdev便可。在啓動本地開發的構建服務的時候,最初只會構建一次,用於訪問其餘項目模塊使用。這樣的代碼是修改其餘模塊並不會被watch,但咱們追求的是開發一個模塊就專心一個模塊的開發便可。這樣才能十分明顯的提升效率。

爲何個人css不更新!!

有這麼一個經典的場景,a君問:

module: {
        loaders: [
            {//抽離css
                test: /\.css$/,
                loader: ExtractTextPlugin.extract({ 
                fallback: 'style-loader', 
                use: 'css-loader', 
                publicPath: '/dist/' })
            }

我代碼這樣寫用了ExtractTextPlugin爲啥更新不了啊!


首先要說,webpack官方直接就告訴你了,咱們在開發環境下,不推薦你用ExtractTextPlugin。由於1是開發環境不須要考慮加載速度和鏈接數量,2是ExtractTextPlugin這個插件也是將option合併之後返回給webpack進行處理,自己輸出的非js模塊,不支持熱替換。這種狀況下直接使用其餘loader打包成js明顯纔是正道(支持熱更新了)。在進行全量構建或者輸出構建的時候再將css添加進去會比較明智。

相關文章
相關標籤/搜索