Webpack 常見靜態資源處理 - 模塊加載器(Loaders)+ExtractTextPlugin插件


Webpack 常見靜態資源處理 - 模塊加載器(Loaders)+ExtractTextPlugin插件

webpack系列目錄

基於webpack搭建純靜態頁面型前端工程解決方案模板, 最終形態源碼見github: https://github.com/ifengkou/webpack-template

正文

Webpack將所有靜態資源都認為是模塊,比如JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,圖片等等,從而可以對其進行統一管理。為此Webpack引入了加載器的概念,除了純JavaScript之外,每一種資源都可以通過對應的加載器處理成模塊。和大多數包管理器不一樣的是,Webpack的加載器之間可以進行串聯,一個加載器的輸出可以成為另一個加載器的輸入。比如LESS文件先通過less-load處理成css,然后再通過css-loader加載成css模塊,最后由style-loader加載器對其做最后的處理,從而運行時可以通過style標簽將其應用到最終的瀏覽器環境。

一 常用loader

安裝css/sass/less loader加載器

cnpm install file-loader css-loader style-loader sass-loader ejs-loader html-loader jsx-loader image-webpack-loader --save-dev

webpack.config.js配置:

module: {
    loaders: [
        {
            test: /\.((woff2?|svg)(\?v=[0-9]\.[0-9]\.[0-9]))|(woff2?|svg|jpe?g|png|gif|ico)$/,
            loaders: [
                // 小於10KB的圖片會自動轉成dataUrl
                'url?limit=10240&name=img/[hash:8].[name].[ext]',
                'image?{bypassOnDebug:true, progressive:true,optimizationLevel:3,pngquant:{quality:"65-80",speed:4}}'
            ]
        },
        {
            test: /\.((ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9]))|(ttf|eot)$/,
            loader: 'url?limit=10000&name=fonts/[hash:8].[name].[ext]'
        },
        {test: /\.(tpl|ejs)$/, loader: 'ejs'},
        {test: /\.css$/, loader: 'style-loader!css-loader'},
        { test: /\.scss$/, loader: 'style!css!sass'}
    ]
},

index.html 新增兩個div

<div class="small-webpack"></div>
<div class="webpack"></div>

index.css 增加兩個圖片,同時將webpack.png(53kb) 和 small-webpack.png(9.8k)

.webpack {
    background: url(../img/webpack.png) no-repeat center;
    height:500px;
}
.small-webpack {
    background: url(../img/small-webpack.png) no-repeat center;
    height:250px;
}

index.js 引入css

require('../css/index.css');

執行webpack指令

$ webpack

查看生成的目錄結構

其中並沒有css文件,css被寫入到了index.js中,index.js 部分截圖

總結:

  • 圖片采用了url-loader加載,如果小於10kb,圖片則被轉化成 base64 格式的 dataUrl
  • css文件被打包進了js文件中

css被打包進了js文件,如果接受不了,可以強制把css從js文件中獨立出來。官方文檔是以插件形式實現:文檔docs點這插件的github點這

二:extract-text-webpack-plugin 插件介紹

Extract text from bundle into a file.從bundle中提取出特定的text到一個文件中。使用 extract-text-webpack-plugin就可以把css從js中獨立抽離出來

安裝

$ npm install extract-text-webpack-plugin --save-dev

使用(css為例)

var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
    module: {
        loaders: [
            { test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") }
        ]
    },
    plugins: [
        new ExtractTextPlugin("styles.css")
    ]
}

它將從每一個用到了require("*.css")的entry chunks文件中抽離出css到單獨的output文件

API

new ExtractTextPlugin([id: string], filename: string, [options])
  • id Unique ident for this plugin instance. (For advanded usage only, by default automatic generated)

  • filename the filename of the result file. May contain [name], [id] and [contenthash].

    • [name] the name of the chunk
    • [id] the number of the chunk
    • [contenthash] a hash of the content of the extracted file
  • options

    • allChunks extract from all additional chunks too (by default it extracts only from the initial chunk(s))
    • disable disables the plugin

    ExtractTextPlugin.extract([notExtractLoader], loader, [options])

根據已有的loader,創建一個提取器(loader的再封裝)

  • notExtractLoader (可選)當css沒有被抽離時,加載器不應該使用(例如:當allChunks:false時,在一個additional 的chunk中)
  • loader 數組,用來轉換css資源的加載器s
  • options
    • publicPath 重寫該加載器(loader)的 publicPath 的設置

多入口文件的extract的使用示例:

let ExtractTextPlugin = require('extract-text-webpack-plugin');

// multiple extract instances
let extractCSS = new ExtractTextPlugin('stylesheets/[name].css');
let extractLESS = new ExtractTextPlugin('stylesheets/[name].less');

module.exports = {
  ...
  module: {
    loaders: [
      {test: /\.scss$/i, loader: extractCSS.extract(['css','sass'])},
      {test: /\.less$/i, loader: extractLESS.extract(['css','less'])},
      ...
    ]
  },
  plugins: [
    extractCSS,
    extractLESS
  ]
};

三:改造項目-抽離css

安裝插件到項目

npm install extract-text-webpack-plugin --save-dev

配置webpack.config.js,加入ExtractTextPlugin和相關處理:

var webpack = require("webpack");
var path = require("path");
var srcDir = path.resolve(process.cwd(), 'src');
var nodeModPath = path.resolve(__dirname, './node_modules');
var pathMap = require('./src/pathmap.json');
var glob = require('glob')
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var entries = function () {
    var jsDir = path.resolve(srcDir, 'js')
    var entryFiles = glob.sync(jsDir + '/*.{js,jsx}')
    var map = {};

    for (var i = 0; i < entryFiles.length; i++) {
        var filePath = entryFiles[i];
        var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'));
        map[filename] = filePath;
    }
    return map;
}

var html_plugins = function () {
    var entryHtml = glob.sync(srcDir + '/*.html')
    var r = []
    var entriesFiles = entries()
    for (var i = 0; i < entryHtml.length; i++) {
        var filePath = entryHtml[i];
        var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'));
        var conf = {
            template: 'html!' + filePath,
            filename: filename + '.html'
        }
        //如果和入口js文件同名
        if (filename in entriesFiles) {
            conf.inject = 'body'
            conf.chunks = ['vendor', filename]
        }
        //跨頁面引用,如pageA,pageB 共同引用了common-a-b.js,那么可以在這單獨處理
        //if(pageA|pageB.test(filename)) conf.chunks.splice(1,0,'common-a-b')
        r.push(new HtmlWebpackPlugin(conf))
    }
    return r
}
var plugins = [];
var extractCSS = new ExtractTextPlugin('css/[name].css?[contenthash]')
var cssLoader = extractCSS.extract(['css'])
var sassLoader = extractCSS.extract(['css', 'sass'])

plugins.push(extractCSS);

plugins.push(new CommonsChunkPlugin({
    name: 'vendor',
    minChunks: Infinity
}));

module.exports = {
    entry: Object.assign(entries(), {
        // 用到什么公共lib(例如jquery.js),就把它加進vendor去,目的是將公用庫單獨提取打包
        'vendor': ['jquery', 'avalon']
    }),
    output: {
        path: path.join(__dirname, "dist"),
        filename: "[name].js",
        chunkFilename: '[chunkhash:8].chunk.js',
        publicPath: "/"
    },
    module: {
        loaders: [
            {
                test: /\.((woff2?|svg)(\?v=[0-9]\.[0-9]\.[0-9]))|(woff2?|svg|jpe?g|png|gif|ico)$/,
                loaders: [
                    //小於10KB的圖片會自動轉成dataUrl,
                    'url?limit=10000&name=img/[hash:8].[name].[ext]',
                    'image?{bypassOnDebug:true, progressive:true,optimizationLevel:3,pngquant:{quality:"65-80",speed:4}}'
                ]
            },
            {
                test: /\.((ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9]))|(ttf|eot)$/,
                loader: 'url?limit=10000&name=fonts/[hash:8].[name].[ext]'
            },
            {test: /\.(tpl|ejs)$/, loader: 'ejs'},
            {test: /\.css$/, loader: cssLoader},
            {test: /\.scss$/, loader: sassLoader}
        ]
    },
    resolve: {
        extensions: ['', '.js', '.css', '.scss', '.tpl', '.png', '.jpg'],
        root: [srcDir, nodeModPath],
        alias: pathMap,
        publicPath: '/'
    },
    plugins: plugins.concat(html_plugins())
}

其中,用ExtractTextPlugin 來抽離css

var ExtractTextPlugin = require('extract-text-webpack-plugin');
var extractCSS = new ExtractTextPlugin('css/[name].css?[contenthash]')
var cssLoader = extractCSS.extract(['css'])
var sassLoader = extractCSS.extract(['css', 'sass'])

plugins.push(extractCSS);
......
//conf - module - loaders
{test: /\.css$/, loader: cssLoader},
{test: /\.scss$/, loader: sassLoader}

注意事項:

  • css中img的路徑會出現問題,通過設置publicPath 解決,采用絕對路徑

      output: {
          ......
          publicPath: "/"
      },
    

運行:

$ webpack

期望

  • css單獨抽離,打包成單獨的css文件
  • html自動引用css文件
  • 小於10k的圖片,轉成base64 格式的 dataUrl
  • webpack.png 會被壓縮,減少文件大小

運行webpack后的項目的目錄結構:

生成的 dist/index.html 自動引用了 index.css 和相關的js,由於設置了publicPath 所以相應的鏈接都采用了絕對路徑

生成的 dist/index.css 小圖片被轉成了data:image形式:

結果:

  • css單獨打包到css目錄
  • html自動注入了link 標簽
  • small-webpack.png 小於10k,被打包進了index.css
  • webpack.png 由原來的50+k 被壓縮成 10- k

最后,運行 webpack-dev-server 看一下運行結果:

總結

Webpack將所有靜態資源都認為是模塊,而通過loader,幾乎可以處理所有的靜態資源,圖片、css、sass之類的。並且通過一些插件如extract-text-webpack-plugin,可以將共用的css抽離出來

下篇介紹改進webpack.config.js:

  • 區分開發環境和生產環境
  • 集成 gulp 實現自動構建打包部署
  • github 發布 前端自動化構建的項目模板


免責聲明!

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



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