webpack的世界

本文也是屢次學習webpack積累下來的知識點,一直在雲筆記裏。javascript

webpack的原理

webpack構建流程
從啓動webpack構建到輸出結果經歷了一系列過程,它們是:css

  • 解析webpack配置參數,合併從shell傳入和webpack.config.js文件裏配置的參數,生產最後的配置結果。
  • 註冊全部配置的插件,好讓插件監聽webpack構建生命週期的事件節點,以作出對應的反應。
  • 從配置的entry入口文件開始解析文件構建AST語法樹,找出每一個文件所依賴的文件,遞歸下去。
  • 在解析文件遞歸的過程當中根據文件類型和loader配置找出合適的loader用來對文件進行轉換。
  • 遞歸完後獲得每一個文件的最終結果,根據entry配置生成代碼塊chunk。
  • 輸出全部chunk到文件系統。

須要注意的是,在構建生命週期中有一系列插件在合適的時機作了合適的事情,好比UglifyJsPlugin會在loader轉換遞歸完後對結果再使用UglifyJs壓縮覆蓋以前的結果html

配置webpack就是在配置一個node的模塊module.exports用於webpack來讀取vue

一、webpack與Rollup的優缺點和區別?

webpack的優勢:java

  • 專一於處理模塊化的項目,能作到開箱及用、一步到位
  • 可用plugin擴展,完整好用又不失靈活
  • 使用場景不限於web開發
  • 社區龐大

webapck的缺點:node

  • 只能用於採用模塊化開發的項目

Rollupreact

  • 能夠對es6進行 Tree Shaking
  • 它支持導出ES模塊的包
  • 它支持程序流分析,能更加正確的判斷項目自己的代碼是否有反作用

二、devserver的使用

webpack-dev-server是一個啓動服務的程序,能夠自動去,讀取webpack.config.js 的文件。執行webpack-dev-server來啓動,就能夠來自動刷新頁面,內部的原理是express的啓服務,用webSocket來通知瀏覽器。jquery

三、webapck配置選項

  • Entry:入口,webpack執行構建的第一步從entry開始
  • Module:模塊,一切都是模塊,一個模塊對應一個文件。webapck從entry開始遞歸找出依賴的模塊。
  • Chunk:代碼塊,一個chunk由多個模塊組合而成,用於代碼合併與分割。
  • Loader:模塊轉換器,將模塊的原內容按照要求轉換成新的內容。
  • Plugin: 擴展插件,在特定時機執行
  • Output:輸出結果,把處理的最終想要的代碼輸出結果

四、Entry的配置

一、context是webpack尋找相對路徑文件會以context爲根目錄
module.exports = {
    context: path.resolve(__dirname, './app'),
    entry:'./main.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './dist')
    }
}
二、Entry
  • string 'app/'
  • array ['app/a.js', 'app/b.js'] 多入口,單Chunk
  • object [a:'a.js', b:'b.js'] 配置多個入口,每一個入口生成一個Chunk

也能夠動態的配置,就是返回函數,能夠同步也能夠異步webpack

同步
entry:()=>{
        return {
            a: './pages/a',
            b:'./pages/a'
        }
    },
異步
    entry:new Promise((resolve)=>{
        resolve({
            a: '/a.js',
            b:'/b.js'
        })
    }),

五、Output配置

output是一個對象,裏面有配置項git

  1. filename 輸出文件的名稱,是string
  • [id Chunk的惟一標識,從0開始]
  • [name Chunk的名稱]
  • [hash] Chunk的惟一標識Hash[hash:8]能夠指定長度
  • [chunkhash] Chunk內容的Hash值,好比 ExtractTextWebapckPlugin就是用的那個
  1. chunkFilename 配置無入口的Chunk的輸出名字,好比CommonChunkPlugin import('path/')動態加載
  2. path輸出文件存在的本地目錄:path: path.resolve(__dirname, './dist')
  3. publicPath 配置上傳cnd的路徑
  4. corssOriginLoading 在異步加載是,利用JONSP的原理動態插入script,用來控制crossorigin的值
    當本地嘗試使用 window.onerror 去記錄腳本的錯誤時,跨域腳本的錯誤只會返回 Script error。
    HTML5 新的規定,是能夠容許本地獲取到跨域腳本的錯誤信息,但有兩個條件:
  • 一是跨域腳本的服務器必須經過 Access-Controll-Allow-Origin 頭信息容許當前域名能夠獲取錯誤信息
  • 二是當前域名的 script 標籤也必須指明 src 屬性指定的地址是支持跨域的地址,也就是 crossorigin 屬性

能夠取一下值:

  • anonymous(默認) 匿名的,加載不會帶上用戶的cookies
  • use-credentials,在加載腳本是會帶上用戶的cokires
  1. libraryTarget 和 library
  2. 構建一個能夠被導入使用的庫
  • libraryTarget 何種方式,var(默認)comomjs/commonjs2/this/window/global
  • library 導出的名字

六、module

  • rules來配置Loader
rules: [{
    test: '/\.js$/',//命中文件
    use: ['babel-loader?cacheDirectory'],//指定loader  解析是從後向前
    include: path.resolve(__dirname, 'src'),//包括 
    exclude: path.resolve(__dirname, 'node_modules')//排除
  },{
    test: /.vue$/,
    use: ['vue-loader']
  }]
  
  
  vue須要使用vue-loader
  • noParse 來忽略沒有采用模塊化的文件的遞歸
noParse: /jquery|chartjs/ //通常是正則 被忽略的文件不該該有import/requie/define等
  • parse能夠細顆粒度配置解析好比AMD,commonJS等

七、resolve 配置webpack如何尋找模塊

resolve: {
    alias: {
        components : './src/components'
    },
    mainFields: ['jsnext:main', 'browser', 'main'],//jsnext 是支持ES6的模式進入   
    extension: ['.ts','.js','.json'],//後綴列表 //延長;延期
    modules: ['./src/components', 'node_modules'],//指定本地解析 import 'button'
    enforceExtension: true,//開啓必須帶後綴  
},

八、plugin 接受數組,plugin的實例

九、devServer 使用devserver啓動還生效

devServer:{
    hot: true,//啓用 webpack 的模塊熱替換特性
    inline: true,//fasle是 iframe 模式
    historyApiFallback: true,//spa history的模式,
        任何請求都會返回index.html 也可使用重寫rewrites來詳細配置
    contentBase:[path.join(__dirname, "public"),
        path.join(__dirname, "assets")],//提供靜態文件 string fales關閉 array
    host:'0.0.0.0',//服務器外部可訪問
    port: '9090',//端口
    disableHostCheck: true,//關閉host,devServer默認是接受本地請求配合host使用
    https:true,//開啓https或者本身導入證書
    compress: true,//默認false開啓gzip壓縮   壓縮
    open: true,//打開瀏覽器
    proxy:{ //   credentials 證書  設置成include,表示容許跨越傳遞cookie
        "/api": "http://localhost:3000"
    }
},

十、其餘配置項

target:'web',//構建到的環境好比node web webworker等
    devtool:'source-map',//  能夠設置爲false
    watch:true,//監聽文件,默認是關閉的devserer默認打開
    externals:{
        jquery: 'jQuery'
    },
    resolveLoader:{ //加載本地loader
},

十一、爲單頁應用生成html

使用 html-webpack-plugin

十二、其餘經常使用Loader

file-loader 將js和css中的圖片等替換成正確的地址,輸出在文件中
url-loader 能夠將文件的內容通過base64編碼後注入js或者css中,可是要限制大小

{
        exclude: [
          /\.(js|jsx)(\?.*)?$/,
          /\.(css|scss)$/,
          /\.json$/,
          /\.bmp$/,
          /\.jpe?g$/,
          /\.png$/,
        ],
        loader: require.resolve('file-loader'),
        options: {
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },
      {
        test: [/\.bmp$/, /\.jpe?g$/, /\.png$/],
        loader: require.resolve('url-loader'),
        options: {
          limit: 10000,
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },

raw-loader 和 svg-inline-loader 能夠把svg內嵌到網頁,把svg當圖片用,能夠用上面的。

1三、Source Map

以方便在瀏覽器經過代碼調試

devtool有不少的取值,由一下6個關鍵字隨意組合而成

  • eval: 用eval語句包裹須要安裝的模塊
  • source-map:生成獨立的Source Map 文件
  • hidden:不在javascript文件中支出source map 文件的所在,這樣瀏覽器就不會自動加載source map
  • inline:將生成的source map轉成base64格式內嵌在javascript文件中
  • cheap:在生成的source map中不會包含列信息,那樣計算量更小,輸出文件更小,同時loader輸出的source map不會被採用
  • module:來時loader的source map 被簡單處理成每行一個模塊

  • eval: 生成代碼 每一個模塊都被eval執行,而且存在@sourceURL
  • cheap-eval-source-map: 轉換代碼(行內) 每一個模塊被eval執行,而且sourcemap做爲eval的一個dataurl
  • cheap-module-eval-source-map: 原始代碼(只有行內) 一樣道理,可是更高的質量和更低的性能
  • eval-source-map: 原始代碼 一樣道理,可是最高的質量和最低的性能
  • cheap-source-map: 轉換代碼(行內) 生成的sourcemap沒有列映射,從loaders生成的sourcemap沒有被使用
  • cheap-module-source-map: 原始代碼(只有行內) 與上面同樣除了每行特色的從loader中進行映射
  • source-map: 原始代碼 最好的sourcemap質量有完整的結果,可是會很慢

eval和.map文件都是sourcemap實現的不一樣方式,雖然大部分sourcemap的實現是經過產生.map文件, 但並不表示只能經過.map文件實現。下面是eval模式後產生的模塊代碼

cheap關鍵字的配置中只有行內,列信息指的是代碼的不包含原始代碼的列信息

  • 在開發環境 用source-map 那個最完整 或者cheap-module-eval-source-map 那個最快
  • 在生產環境就flase 或者 hidden-source-map 爲錯誤蒐集用
  • 若是須要加載模塊的source-map 須要使用source-map-loader
  • vue-cli 用的devtool: '#eval-source-map',

1四、代碼檢測和npm配合

與npm 配着
npm 能夠運行node__modules裏安裝的程序
能夠縮短命令
代碼檢測

javascript 用eslint 用.eslintrc json 文件配置 eslint-loader enforce: 'pre'
ts 用TSlint tslint.json 配置 tslint-loader
css stylelint 用.stylelintrc 配置 StyleLintPlugin 插件來處理

代碼檢測會變慢構建速度,能夠配置IDE來檢測,同時配置git Hook在代碼提交時檢測

1五、node運行webapck和中間件

var config = require('../config')
var compiler = webpack(webpackConfig)

調用compiler的watch能夠監聽文件變化

require('webpack-dev-middleware')是express的一個插件

var app = express()
var compiler = webpack(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true,
    chunks: false
  }
})

可是不支持hot模式須要require('webpack-hot-middleware')

app.use(hotMiddleware)

代理使用

var proxyMiddleware = require('http-proxy-middleware')

1六、webapck構建優化

一、減少文件的搜索範圍
  1. 優化loader配置,利用好include和exclude
  2. 優化resolve.modules 使用絕對路徑
modules: [path.resolve(__dirname, 'node_modules')]
  1. alias 指向 x.min.js 文件,可是lodash不適合
alias: {
        react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')
    },
  1. noParse 配置不解析
noParse: /jquery|chartjs/ //通常是正則 被忽略的文件不該該有import/require/define等
二、優化構建過程
  1. babel-loader 加 cacheDirectory
  2. DllPlugin 官方推薦作法是把不常變更的文件打DLL

工程就把 react react-dom prop-types classnames mobx mobx-react lodash moment polyfill 等打進來 可使用 [npm version]_dll.js 用 npm version 的話只要 version 一改變咱們會從新打包,好比升級了 react ,咱們就會 version +,就會從新打包。

先建立一個webpack.config.dll.js

執行webpack --config ./webpack.config.dll.js把須要dll的文件輸出到dist/dll

const webpack = require('webpack');
const path = require('path');

const {version} = require('./package.json');

module.exports = {
    entry: {
        'react': [
            'react', 'react-dom',
            'prop-types',
            'classnames',
            'lodash', 'moment'
        ]
    },
    output: {
        path: path.join(__dirname, 'dist/dll'),
        filename: `[name].${version}.js`,
        library: 'dll_[name]',
        publicPath: '/dist/dll/'
    },
    plugins: [
        new webpack.DllPlugin({
            path: path.join(__dirname, 'dist/dll/', '[name].manifest.json'),
            name: 'dll_[name]'
        })
    ]
};

在webapck配置文件中使用

new webpack.DllReferencePlugin({
            context: __dirname,
            // 在這裏引入 manifest 文件
            manifest: require('./dist/dll/react.manifest.json')
        }),
//把js插入到html文件中
new AddAssetHtmlPlugin({
    filepath: require.resolve(`./dist/dll/${getDLLFileName()}`),
    outputPath: 'dll',
    includeSourcemap: false,
    hash: true,
    publicPath: '/dist/dll/'
})

原理就是提早構建,緩存起來,用window的全局變量來取。

  1. happypack 對 build 的速度大大大提示,能夠多線程打包,cache 也讓 rebuild 加快
{
    test: /\.(js|jsx)$/,//命中文件
    use: 'happypack/loader?id=js',//指定loader
    include: path.resolve(__dirname, 'src'),//包括
    exclude: path.resolve(__dirname, 'node_modules')//排除
   }, {
    test: /\.(css|scss)$/,
    loader: 'happypack/loader?id=css'
}
new HappyPack({
    id: 'js',
    threadPool: happyThreadPool,
    loaders: [{
        path: 'babel-loader',
        query: {
            cacheDirectory: true
        }
    }]
}),
new HappyPack({
    id: 'css',
    threadPool: happyThreadPool,
    loaders: ['style-loader','css-loader', 'sass-loader']
}),

原理:happypack 的原理是讓loader能夠多進程去處理文件,css和js,圖片和文件支持很差

  1. Devtool 使用 開發用cheap-module-eval-source-map 那個最快

  2. 壓縮 UglifyJsParallelPlugin在webpack2.0之後支持並行,就能夠棄用
  3. 開啓自動刷新(iframe會比inline快一點)和熱更新熱替換(hot 開啓) 優化就是把node_modules 排除
  4. 區分環境

if (isDev) {
    config.plugins.push(new webpack.NamedModulesPlugin()); //顯示模塊更新名字
    config.plugins.push(new webpack.HotModuleReplacementPlugin()); //hot更新插件

    config.devServer = {
        hot: true,
        contentBase: './',
        historyApiFallback: {
            index: "/build/index.html"
        },
        publicPath: '/build/',
        host: '0.0.0.0'
    };
    config.devtool = 'eval';
} else {
    config.plugins.push(new webpack.optimize.UglifyJsPlugin({ // 開啓壓縮
        cache: true,
        parallel: true,
        compress: {
            warning: fasle,// 是否刪除一個警告信息fasle刪除
            drop_console: true,//刪除console
            collapse_vars: true,是否內嵌雖然已定義可是隻用到一次的變量。
            reduce_vars: true ,提取屢次的變量
        },
        output: {
            comments: false,//是否刪除註釋,默認是不刪除,設置爲false,刪除全部的註釋
            beautify: fasle, //保留空格和製表符  建議false關閉,最緊湊的輸出
        }
        
        
        
    }));

    config.devtool = '#source-map';
}

八、壓縮css 在css-loader開啓minimize選項 和 提取css到單獨的文件

{
    test: /\.(css|less)$/,
    loader: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: 'happypack/loader?id=css'
    })
},
new ExtractTextPlugin('css/[name].[contenthash:8].css')

九、在webapck中接入cdn

  • 在output.publicPath 中設置javascript的地址
  • 在css-loader.publicPath 中設置css文件中導入的資源的地址
  • 在HtmlWebpackPlugin或者WebPlugin.stylePublicPath 設置css單獨是文件的資源

十、Tree Shaking 去除重複代碼 ,webapck沒有程序流分析,避免不了babel產生的反作用

十一、以模塊化來引入

// 原來的引入方式
 import {debounce} from 'lodash';

//按模塊化的引入方式
import debounce from 'lodash/debounce';

十二、使用異步的模塊加載
require.ensure來設置哪些模塊須要異步加載,webpack會將它打包到一個獨立的chunk中

$('.bg-input').click(() => {
    console.log('clicked, loading async.js')

    require.ensure([], require => {

        require('./components/async2').log();
        require('./components/async1').log();
        console.log('loading async.js done');
    });
});

import(*) 是新的按需加載,在react-router4中不能使用require.ensure要使用getAsyncComponent函數

component = {
    getAsyncComponent(() => {
        import('./pages/login')
    })
}

1三、Scope Hoisting 做用域提高 webpack3的新功能

使用ModuleConcatenationPlugin插件來加快JS執行速度
這是webpack3的新特性(Scope Hoisting),實際上是借鑑了Rollup打包工具來的,它將一些有聯繫的模塊,放到一個閉包函數裏面去,經過減小閉包函數數量從而加快JS的執行速度

new webpack.optimize.ModuleConcatenationPlugin({

        })

原理:原理其實很簡單,分析模塊之間的依賴關係,儘量將被打散的模塊合併到一個函數中,前提是不能形成代碼冗餘,源碼必須採用es6語句。

1四、提取公共代碼
使用CommonsChunkPlugin提取公共的模塊,能夠減小文件體積,也有助於瀏覽器層的文件緩存,仍是比較推薦的,那個是在最後打包的時候用的。

// 提取公共模塊文件
        new webpack.optimize.CommonsChunkPlugin({
            chunks: ['home', 'detail'],
            // 開發環境下須要使用熱更新替換,而此時common用chunkhash會出錯,能夠直接不用hash
            filename: '[name].js' + (isProduction ? '?[chunkhash:8]' : ''),
            name: 'common'
        }),




// 切合公共模塊的提取規則,有時後你須要明確指定默認放到公共文件的模塊
// 文件入口配置
    entry: {
        home: './src/js/home',
        detail: './src/js/detail',
        // 提取jquery入公共文件
        common: ['jquery', 'react', 'react-dom']
    },
    
     entry: {index:'./src/index.js',vendor: ['react','react-dom','react-router']},
    
    new webpack.optimize.CommonsChunkPlugin({
          name: "vendor",
          filename: "vendor.js",
      }),

1五、prepack

利用抽象語法樹(AST)來分析源碼,過來問題大大,不建議使用

const PrepackWebpackPlugin = require('prepack-webapck-plugin').default;

module.exports = {
    plugins: [
        new PrepackWebpackPlugin()
    ]

}

1六、可視化的輸出分析 Analyse webpack-bundle-analyzer

1七、pwa構建

用 Webpack 構建接入 Service Workers 的離線應用要解決的關鍵問題在於如何生成上面提到的 sw.js 文件, 而且sw.js文件中的 cacheFileList 變量,表明須要被緩存文件的 URL 列表,須要根據輸出文件列表所對應的 URL 來決定,而不是像上面那樣寫成靜態值。

假如構建輸出的文件目錄結構爲:

├── app_4c3e186f.js
├── app_7cc98ad0.css
└── index.html

那麼 sw.js 文件中 cacheFileList 的值應該是:

var cacheFileList = [
  '/index.html',
  'app_4c3e186f.js',
  'app_7cc98ad0.css'
];

Webpack 沒有原生功能能完成以上要求,幸虧龐大的社區中已經有人爲咱們作好了一個插件 serviceworker-webpack-plugin 能夠方便的解決以上問題。 使用該插件後的 Webpack 配置以下:

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { WebPlugin } = require('web-webpack-plugin');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');

module.exports = {
  entry: {
    app: './main.js'// Chunk app 的 JS 執行入口文件
  },
  output: {
    filename: '[name].js',
    publicPath: '',
  },
  module: {
    rules: [
      {
        test: /\.css/,// 增長對 CSS 文件的支持
        // 提取出 Chunk 中的 CSS 代碼到單獨的文件中
        use: ExtractTextPlugin.extract({
          use: ['css-loader'] // 壓縮 CSS 代碼
        }),
      },
    ]
  },
  plugins: [
    // 一個 WebPlugin 對應一個 HTML 文件
    new WebPlugin({
      template: './template.html', // HTML 模版文件所在的文件路徑
      filename: 'index.html' // 輸出的 HTML 的文件名稱
    }),
    new ExtractTextPlugin({
      filename: `[name].css`,// 給輸出的 CSS 文件名稱加上 Hash 值
    }),
    new ServiceWorkerWebpackPlugin({
      // 自定義的 sw.js 文件所在路徑
      // ServiceWorkerWebpackPlugin 會把文件列表注入到生成的 sw.js 中
      entry: path.join(__dirname, 'sw.js'),
    }),
  ],
  devServer: {
    // Service Workers 依賴 HTTPS,使用 DevServer 提供的 HTTPS 功能。
    https: true,
  }
};

1八、多頁

AutoWebPlugin插件來配置多頁
├── pages
│   ├── index
│   │   ├── index.css // 該頁面單獨須要的 CSS 樣式
│   │   └── index.js // 該頁面的入口文件
│   └── login
│       ├── index.css
│       └── index.js
├── common.css // 全部頁面都須要的公共 CSS 樣式
├── google_analytics.js
├── template.html
└── webpack.config.js
  • 全部單頁應用的代碼都須要放到一個目錄下,例如都放在 pages 目錄下;
  • 一個單頁應用一個單獨的文件夾,例如最後生成的 index.html 相關的代碼都在 index 目錄下,login.html 同理;
  • 每一個單頁應用的目錄下都有一個 index.js 文件做爲入口執行文件。

AutoWebPlugin 強制性的規定了項目部分的目錄結構,在pages下,每個文件夾就是一個目錄。經過插件自動生成手動須要配置的兩個html插件。

const { AutoWebPlugin } = require('web-webpack-plugin');

// 使用本文的主角 AutoWebPlugin,自動尋找 pages 目錄下的全部目錄,把每個目錄當作一個單頁應用
const autoWebPlugin = new AutoWebPlugin('pages', {
  template: './template.html', // HTML 模版文件所在的文件路徑
  postEntrys: ['./common.css'],// 全部頁面都依賴這份通用的 CSS 樣式文件
  // 提取出全部頁面公共的代碼
  commonsChunk: {
    name: 'common',// 提取出公共代碼 Chunk 的名稱
  },
});

module.exports = {
  // AutoWebPlugin 會爲尋找到的全部單頁應用,生成對應的入口配置,
  // autoWebPlugin.entry 方法能夠獲取到全部由 autoWebPlugin 生成的入口配置
  entry: autoWebPlugin.entry({
    // 這裏能夠加入你額外須要的 Chunk 入口
  }),
  plugins: [
    autoWebPlugin,
  ],
};

template.html 模版文件以下:

<html>
<head>
  <meta charset="UTF-8">
  <!--在這注入該頁面所依賴但沒有手動導入的 CSS-->
  <!--STYLE-->
  <!--注入 google_analytics 中的 JS 代碼-->
  <script src="./google_analytics.js?_inline"></script>
  <!--異步加載 Disqus 評論-->
  <script src="https://dive-into-webpack.disqus.com/embed.js" async></script>
</head>
<body>
<div id="app"></div>
<!--在這注入該頁面所依賴但沒有手動導入的 JavaScript-->
<!--SCRIPT-->
<!--Disqus 評論容器-->
<div id="disqus_thread"></div>
</body>
</html>

在模版中生成

  • CSS 類型的文件注入到 所在的位置,若是 不存在就注入到 HTML HEAD 標籤的最後;
  • JavaScrip 類型的文件注入到 所在的位置,若是 不存在就注入到 HTML BODY 標籤的最後。
經過gulp來管理html,webapck來處理js也是解決多頁的好方法
利用html-webpack-plugin 插件循環生成
// 引入插件
const HTMLWebpackPlugin = require("html-webpack-plugin");
// 引入多頁面文件列表
const { HTMLDirs } = require("./config");
// 經過 html-webpack-plugin 生成的 HTML 集合
let HTMLPlugins = [];
// 入口文件集合
let Entries = {}

// 生成多頁面的集合
HTMLDirs.forEach((page) => {
    const htmlPlugin = new HTMLWebpackPlugin({
        filename: `${page}.html`,
        template: path.resolve(__dirname, `../app/html/${page}.html`),
        chunks: [page, 'commons'],
    });
    HTMLPlugins.push(htmlPlugin);
    Entries[page] = path.resolve(__dirname, `../app/js/${page}.js`);
})

1九、同構

構建服務端渲染

服務端渲染的代碼要運行在nodejs環境,和瀏覽器不一樣的是,服務端渲染代碼須要採用commonjs規範同時不該該包含除js以外的文件好比css。webpack配置以下:

module.exports = {
  target: 'node',
  entry: {
    'server_render': './src/server_render',
  },
  output: {
    filename: './dist/server/[name].js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
      },
      {
        test: /\.(scss|css|pdf)$/,
        loader: 'ignore-loader',
      },
    ]
  },
};

其中幾個關鍵的地方在於:

target: 'node' 指明構建出的代碼是要運行在node環境裏
libraryTarget: 'commonjs2' 指明輸出的代碼要是commonjs規範
{test: /.(scss|css|pdf)$/,loader: 'ignore-loader'} 是爲了防止不能在node裏執行服務端渲染也用不上的文件被打包進去。

20、laoder與plugins插件編寫

若是你的擴展是想對一個個單獨的文件進行轉換那麼就編寫loader剩下的都是plugin

其中對文件進行轉換能夠是像:

  • babel-loader把es6轉換成es5
  • file-loader把文件替換成對應的URL
  • raw-loader注入文本文件內容到代碼裏去

編寫loader很是簡單,以comment-require-loader爲例:

module.exports = function (content) {
    return replace(content);
};

loader的入口須要導出一個函數,這個函數要乾的事情就是轉換一個文件的內容。
函數接收的參數content是一個文件在轉換前的字符串形式內容,須要返回一個新的字符串形式內容做爲轉換後的結果,全部經過模塊化倒入的文件都會通過loader。從這裏能夠看出loader只能處理一個個單獨的文件而不能處理代碼塊

class EndWebpackPlugin {

constructor(doneCallback, failCallback) {
    this.doneCallback = doneCallback;
    this.failCallback = failCallback;
}

apply(compiler) { 彙編者; 編輯者; 編纂者 
    // 監聽webpack生命週期裏的事件,作相應的處理
    compiler.plugin('done', (stats) => {
        this.doneCallback(stats);
    });
    compiler.plugin('failed', (err) => {
        this.failCallback(err);
    });
}

}

module.exports = EndWebpackPlugin;
loader的入口須要導出一個class, 在new EndWebpackPlugin()的時候經過構造函數傳入這個插件須要的參數,在webpack啓動的時候會先實例化plugin再調用plugin的apply方法,插件須要在apply函數裏監聽webpack生命週期裏的事件,作相應的處理。
webpack plugin 裏有2個核心概念:

Compiler: 從webpack啓動到推出只存在一個Compiler,Compiler存放着webpack配置
Compilation: 因爲webpack的監聽文件變化自動編譯機制,Compilation表明一次編譯。
Compiler 和 Compilation 都會廣播一系列事件。
webpack生命週期裏有很是多的事件能夠在event-hooks和Compilation裏查到

2一、經常使用loader與plugins

加載文件
  • raw-loader:把文本文件的內容加載到代碼中去,在 3-20加載SVG 中有介紹。
  • file-loader:把文件輸出到一個文件夾中,在代碼中經過相對 URL 去引用輸出的文件,在 3-19加載圖片、3-20加載 SVG、4-9 CDN 加速 中有介紹。
  • url-loader:和 file-loader 相似,可是能在文件很小的狀況下以 base64 的方式把文件內容注入到代碼中去,在 3-19加載圖片、3-20加載 SVG 中有介紹。
  • source-map-loader:加載額外的 Source Map 文件,以方便斷點調試,在 3-21加載 Source Map 中有介紹。
  • svg-inline-loader:把壓縮後的 SVG 內容注入到代碼中,在 3-20加載 SVG 中有介紹。
  • node-loader:加載 Node.js 原生模塊 .node 文件。
  • image-loader:加載而且壓縮圖片文件。
  • json-loader:加載 JSON 文件。
  • yaml-loader:加載 YAML 文件。

    編譯模版
  • pug-loader:把 Pug 模版轉換成 JavaScript 函數返回。
  • handlebars-loader:把 Handlebars 模版編譯成函數返回。
  • ejs-loader:把 EJS 模版編譯成函數返回。
  • haml-loader:把 HAML 代碼轉換成 HTML。
  • markdown-loader:把 Markdown 文件轉換成 HTML。

    轉換腳本語言
  • babel-loader:把 ES6 轉換成 ES5,在3-1使用 ES6 語言中有介紹。
  • ts-loader:把 TypeScript 轉換成 JavaScript,在3-2使用 TypeScript 語言中有遇到。
  • awesome-typescript-loader:把 TypeScript 轉換成 JavaScript,性能要比 ts-loader 好。
  • coffee-loader:把 CoffeeScript 轉換成 JavaScript。

    轉換樣式文件
  • css-loader:加載 CSS,支持模塊化、壓縮、文件導入等特性。
  • style-loader:把 CSS 代碼注入到 JavaScript 中,經過 DOM 操做去加載 CSS。
  • sass-loader:把 SCSS/SASS 代碼轉換成 CSS,在3-4使用 SCSS 語言中有介紹。
  • postcss-loader:擴展 CSS 語法,使用下一代 CSS,在3-5使用 PostCSS中有介紹。
  • less-loader:把 Less 代碼轉換成 CSS 代碼。
  • stylus-loader:把 Stylus 代碼轉換成 CSS 代碼。

    檢查代碼
  • eslint-loader:經過 ESLint 檢查 JavaScript 代碼,在 3-16檢查代碼中有介紹。
  • tslint-loader:經過 TSLint 檢查 TypeScript 代碼。
  • mocha-loader:加載 Mocha 測試用例代碼。
  • coverjs-loader:計算測試覆蓋率。

    其它
  • vue-loader:加載 Vue.js 單文件組件,在3-7使用 Vue 框架中有介紹。
  • i18n-loader:加載多語言版本,支持國際化。
  • ignore-loader:忽略掉部分文件,在3-11構建同構應用中有介紹。
  • ui-component-loader:按需加載 UI 組件庫,例如在使用 antd UI 組件庫時,不會由於只用到了 Button 組件而打包進全部的組件。

用於修改行爲
  • define-plugin:定義環境變量,在4-7區分環境中有介紹。
  • context-replacement-plugin:修改 require 語句在尋找文件時的默認行爲。
  • ignore-plugin:用於忽略部分文件。

    用於優化
  • commons-chunk-plugin:提取公共代碼,在4-11提取公共代碼中有介紹。
  • extract-text-webpack-plugin:提取 JavaScript 中的 CSS 代碼到單獨的文件中,在1-5使用 Plugin 中有介紹。
  • prepack-webpack-plugin:經過 Facebook 的 Prepack 優化輸出的 JavaScript 代碼性能,在 4-13使用 Prepack 中有介紹。
  • uglifyjs-webpack-plugin:經過 UglifyES 壓縮 ES6 代碼,在 4-8壓縮代碼中有介紹。
  • webpack-parallel-uglify-plugin:多進程執行 UglifyJS 代碼壓縮,提高構建速度。
  • imagemin-webpack-plugin:壓縮圖片文件。
  • webpack-spritesmith:用插件製做雪碧圖。
  • ModuleConcatenationPlugin:開啓 Webpack Scope Hoisting 功能,在4-14開啓 ScopeHoisting中有介紹。
  • dll-plugin:借鑑 DDL 的思想大幅度提高構建速度,在4-2使用 DllPlugin中有介紹。
  • hot-module-replacement-plugin:開啓模塊熱替換功能。

    其它
  • serviceworker-webpack-plugin:給網頁應用增長離線緩存功能,在3-14 構建離線應用中有介紹。
  • stylelint-webpack-plugin:集成 stylelint 到項目中,在3-16檢查代碼中有介紹。
  • i18n-webpack-plugin:給你的網頁支持國際化。
  • provide-plugin:從環境中提供的全局變量中加載模塊,而不用導入對應的文件。
  • web-webpack-plugin:方便的爲單頁應用輸出 HTML,比 html-webpack-plugin 好用。

22 webpack4

  • webpack 4 更快(速度提高98%)
  • 新增Mode選項,區分環境
  • 零配置以及默認值
  • 再見 CommonsChunkPlugin =》 新 API optimize.splitChunks
  • WebAssembly 支持
  • .mjs 支持
相關文章
相關標籤/搜索