天天看點

webpack初步了解webpack初步了解

webpack初步了解

  • webpack初步了解
    • 建構工具
      • 建構工具的功能
      • 常用的建構工具
    • webpack介紹
      • webpack指令
    • webpack常用配置介紹
      • 出入口 Entry&Output
        • entry配置
        • output的配置
      • 子產品 Module
      • 子產品加載(轉換)器 Loader
      • loader學習
        • 使用loader的三種方式
        • babel
      • 插件 Plugin
    • webpack-dev-server
      • devServer配置項
    • webpack插件
      • extract-text-webpack-plugin
    • 開發/生産環境打包
    • webpack項目中的最佳配置

建構工具

建構工具的功能

  • 代碼轉換:TypeScript 編譯成 JavaScript、SCSS 編譯成 CSS 等。
  • 檔案優化:壓縮 JavaScript、CSS、HTML 代碼,壓縮合并圖檔等。
  • 子產品合并:在采用子產品化的項目裡會有很多個子產品和檔案,需要建構功能把子產品分類合并成一個檔案。
  • 代碼分割:提取多個頁面的公共代碼、提取首屏不需要執行部分的代碼讓其異步加載。
  • 自動重新整理:監聽本地源代碼的變化,自動重新建構、重新整理浏覽器。
  • 代碼校驗:在代碼被送出到倉庫前需要校驗代碼是否符合規範,以及單元測試是否通過。

常用的建構工具

  • grunt
  • gulp
  • fis3(百度)
  • webpack子產品化管理工具,可以對子產品進行:
    • 壓縮
    • 預處理
    • 按需打包
    • 按需加載
    • 熱加載

webpack介紹

  • webpack可以看做是子產品打包機,它做的事情是:
    • 分析你的項目結構,找到JavaScript子產品;
    • 其它的一些浏覽器不能直接運作的拓展語言(Scss,TypeScript等);
    • 并将其轉換和打包為合适的格式供浏覽器使用;
  • Webpack和Grunt以及Gulp相比有什麼特性
    • Webpack和另外兩個并沒有太多的可比性;
    • Gulp/Grunt是一種能夠優化前端的開發流程的工具,而WebPack是一種子產品化的解決方案;
    • Webpack的優點使得Webpack在很多場景下可以替代Gulp/Grunt類的工具;
    • Grunt和Gulp的工作方式是:在一個配置檔案中,指明對某些檔案進行類似編譯,組合,壓縮等任務的具體步驟,工具之後可以自動替你完成這些任務。
    • Webpack的工作方式是:把你的項目當做一個整體,通過一個給定的主檔案(如:index.js),Webpack将從這個檔案開始找到你的項目的所有依賴檔案,使用loaders處理它們,最後打包為一個(或多個)浏覽器可識别的JavaScript檔案。
  • webpack特征
    • 插件化:webpack本身非常靈活,提供了豐富的插件接口。基于這些接口,webpack開發了很多插件作為内置功能
    • 速度快:webpack使用異步IO以及多級緩存機制。是以webpack的速度是很快的,尤其是增量更新。
    • 豐富的Loaders:loaders用來對檔案做預處理。這樣webpack就可以打包任何靜态檔案。
    • 高适配性:webpack同時支援AMD/CommonJs/ES6子產品方案。webpack會靜态解析你的代碼,自動幫你管理他們的依賴關系。此外,webpack對第三方庫的相容性很好。
    • 代碼拆分:webpack可以将你的代碼分片,進而實作按需打包。這種機制可以保證頁面隻加載需要的JS代碼,減少首次請求的時間。
    • 優化:webpack提供了很多優化機制來減少打包輸出的檔案大小,不僅如此,它還提供了hash機制,來解決浏覽器緩存問題。
    • 開發模式友好:webpack為開發模式也提供了很多輔助功能。比如SourceMap、熱更新等。
    • 使用場景多:webpack不僅适用于web應用場景,也适用于Webworkers、Node.js場景

webpack指令

  • webpack 執行一次開發時的編譯
  • webpack -p 執行一次生成環境的編譯(壓縮)
  • webpack –watch 在開發時持續監控增量編譯(很快)
  • webpack -d 讓他生成SourceMaps
  • webpack –progress 顯示編譯進度
  • webpack –colors 顯示靜态資源的顔色
  • webpack –display-chunks 展示編譯後的分塊
  • webpack –display-modules 列出打包子產品
  • webpack –display-reasons 顯示更多引用子產品原因
  • webapck –display-error-details 顯示更多報錯資訊

webpack常用配置介紹

出入口 Entry&Output

  • entry:入口
    • 關聯的很多其他需要打包的檔案
  • output:出口
    • path:檔案打出的路徑,這個path是nodejs内置的方法;

      entry和output配置

entry配置

  • entry是指需要打包的檔案;
  • entry有幾種使用方法:
    • 字元串
entry:__dirname+'/src/script/main.js'
//對應
output:{
  path:__dirname+'/dist/js',
  filename:'bundle.js'
}
           
  • 數組
entry:[
  __dirname+'/src/script/main.js',
  __dirname+'/src/script/a.js'
]
//對應
output:{
  path:__dirname+'/dist/js',
  filename:'bundle.js'
}
           
  • 對象
entry:{
        main:__dirname+'/src/script/main.js',
        a:__dirname+'/src/script/a.js'
    }
//對應
output:{
  path:__dirname+'/dist/js',
  filename:'[name]-[hash].js'
}
//or 
output:{path:__dirname+'/dist/js',filename:'[name]-[chunkhash].js'}
           

output的配置

  • output是指打包生成的檔案
  • entry中輸入多個chunk時,為確定檔案名唯一避免互相覆寫使用占位符命名filename;
  • 三種占位符
    • [name]是chunk的name;
    • [hash]是本次打包的hash值,hash值相同;
    • [chunkhash]是每個chunk的hash值,不同檔案同次打包不相同,保證檔案的唯一性,隻有改變檔案中的内容時hash才變化,未做改變的檔案hash值不變;

子產品 Module

  • module:子產品,在 Webpack眼裡一切皆子產品,預設隻識别js檔案, 如果是其它類型檔案利用對應的loader轉換為js子產品。
    • rules:做很多的規定,比如:加載css、将es6編譯成es5;
    • “-loader”其實是可以省略不寫的,多個loader之間用“!”連接配接起來
    module: {
       //加載器配置
       loaders: [
           //.css 檔案使用 style-loader 和 css-loader 來處理
           { test: /\.css$/, loader: 'style-loader!css-loader' },
           //.js 檔案使用 jsx-loader 來編譯處理
           { test: /\.js$/, loader: 'jsx-loader?harmony' },
           //.scss 檔案使用 style-loader、css-loader 和 sass-loader 來編譯處理
           { test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
           //圖檔檔案使用 url-loader 來處理,小于8kb的直接轉為base64
           { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
       ]
    }
               
    • webpack本身隻能加載js子產品,如果需要加載其他類型的檔案(子產品),就需要使用對應的loader進行轉換/加載;
    • 如果我們想要在js檔案中通過require引入子產品,比如css或image,那麼就需要在這裡配置加載器,這一點對于React來說相當友善,因為可以在元件中使用子產品化CSS。而一般的項目中可以不用到這個加載器。
    • module 的作用是添加loaders, 那loaders有什麼作用呢?

子產品加載(轉換)器 Loader

  • loader:子產品加載器,将非js子產品包裝成webpack能了解的js子產品;
  • loader用于轉換應用程式的資源檔案。他們是運作在nodejs下的函數,使用參數來擷取一個資源的來源,并且傳回一個新的來源(資源的位置)。
  • 檔案loader:
    • url-loader 像 file loader 一樣工作,但如果檔案小于限制,可以傳回 data URL
    • file-loader 将檔案發送到輸出檔案夾,并傳回(相對)URL
  • JSON的loader
    • json-loader 加載 JSON 檔案(預設包含)
  • ES5-6的loader
    • babel-loader 加載 ES2015+ 代碼,然後使用 Babel 轉譯為 ES5
  • css的loader
    • style-loader 将子產品的導出作為樣式添加到 DOM 中
    • css-loader 解析 CSS 檔案後,使用 import 加載,并且傳回 CSS 代碼
    • less-loader 加載和轉譯 LESS 檔案
    • sass-loader 加載和轉譯 SASS/SCSS 檔案
    • stylus-loader 加載和轉譯 Stylus 檔案
  • 代碼規範loader
    • eslint-loader PreLoader,使用 ESLint 清理代碼
  • vue的loader
    • vue-loader 加載和轉譯 Vue 元件
  • resolve
    • alias可以用于定義别名,用過seajs等子產品工具的都知道alias的作用,比如我們在這裡定義了ui這個别名,那麼在子產品中想引用ui目錄下的檔案,就可以直接這樣寫:

      require('ui/dialog.js');

      不用加上前面的更長的檔案路徑。
resolve: {
    //查找module的話從這裡開始查找
    root: 'E:/github/flux-example/src', //絕對路徑
    //自動擴充檔案字尾名,意味着我們require子產品可以省略不寫字尾名
    extensions: ['', '.js', '.json', '.scss'],
    //子產品别名定義,友善後續直接引用别名,無須多寫長長的位址
    alias: {
        AppStore : 'js/stores/AppStores.js',//後續直接 require('AppStore') 即可
        ActionType : 'js/actions/ActionType.js',
        AppAction : 'js/actions/AppAction.js'
    }
}
           

loader學習

  • 學習網址:https://webpack.js.org/concepts/loaders/;
  • Webpack 本身隻能處理原生的js子產品,但是loader轉換器可以将各種類型的資源轉換成js子產品。這樣,任何資源都可以成為Webpack可以處理的子產品。

使用loader的三種方式

  • 在檔案中直接引入loader檔案,es6的文法
//es6的文法
import Styles from 'style-loader!css-loader?modules!./styles.css';
//commonjs文法
require('style-loader!css-loader?modules!./styles.css')
           
  • 在指令行界面使用的方式CLI
//直接執行
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
           
  • 在配置檔案中使用loader
module: {
    rules: [
      {
        <!-- 首先對資源一個正則比對 -->
        test: /\.css$/,
        <!-- 比對成功後會使用多個loader對其進行處理 -->
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
    ]
  }
           

babel

  • Babel通過文法轉換器支援最新版本的JavaScript。這些插件允許你立刻使用新文法,無需等待浏覽器支援。
  • 在src檔案夾下建立component檔案夾,component裡面建立layer元件,元件裡有js、css、html檔案;
  • 在src檔案夾中建立入口js檔案,app.js;
//app.js中引入html
import layer from './component/layer/layer.js'
const App = function() {
    console.log(layer)
}
new App()

//layer.js中渲染模闆
import tpl from './layer.html'
function layer() {
    return {
        name: 'layer',
        tpl: tpl
    }
}
export default layer;
           
  • 安裝babel轉化es6的代碼

    npm install --save-dev babel-loader babel-core

    (webpack3版本)
  • 使用babel加載器将es6的文法進行轉化,轉化時要指定參數,方式有三種:
    • 使用配置指定
//在配置webpack.config.js中添加
module: {
        rules: [{
            test: /\.js$/,
            loader: "babel-loader",
            //加快打包速度的配置
            //排除範圍
            exclude: __dirname + '/node_modules/',
            //babel-loader的處理範圍
            include: '/src/',
            //webpack3中使用options代替query
            options: {
                'presets': ['env']
            }
        }]
    }
           
  • 在package.json中添加
"babel":{
  "presets":["env"];
}
           
  • 根目錄下建立.babelrc檔案
{
  "presets": ["env"]
} 
           

插件 Plugin

  • plugin:插件,在webpack建構流程中的特定時機插入具有特定功能的代碼;
    webpack初步了解webpack初步了解
//使用了CommonsChunkPlugin用于生成公用代碼,不隻可以生成一個,還能根據不同頁面的檔案關系,自由生成多個,例如:
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
    entry: {
        p1: "./page1",
        p2: "./page2",
        p3: "./page3",
        ap1: "./admin/page1",
        ap2: "./admin/page2"
    },
    output: {
        filename: "[name].js"
    },
    plugins: [
        new CommonsChunkPlugin("admin-commons.js", ["ap1", "ap2"]),
        new CommonsChunkPlugin("commons.js", ["p1", "p2", "admin-commons.js"])
    ]
};
// 在不同頁面用<script>标簽引入如下js:
// page1.html: commons.js, p1.js
// page2.html: commons.js, p2.js
// page3.html: p3.js
// admin-page1.html: commons.js, admin-commons.js, ap1.js
// admin-page2.html: commons.js, admin-commons.js, ap2.js
           
  • 例:
module.exports = {
    devtool: "source-map",    //生成sourcemap,便于開發調試
    entry: getEntry(),         //擷取項目入口js檔案
    output: {
        path: path.join(__dirname, "dist/js/"), //檔案輸出目錄
        publicPath: "dist/js/",        //用于配置檔案釋出路徑,如CDN或本地伺服器
        filename: "[name].js",        //根據入口檔案輸出的對應多個檔案名
    },
    module: {
        //各種加載器,即讓各種檔案格式可用require引用
        loaders: [
            // { test: /\.css$/, loader: "style-loader!css-loader"},
            // { test: /\.less$/, loader: "style-loader!csss-loader!less-loader"}
        ]
    },
    resolve: {
        //配置别名,在項目中可縮減引用路徑
        alias: {
            jquery: srcDir + "/js/lib/jquery.min.js",
            core: srcDir + "/js/core",
            ui: srcDir + "/js/ui"
        }
    },
    plugins: [
        //提供全局的變量,在子產品中使用無需用require引入
        new webpack.ProvidePlugin({
            jQuery: "jquery",
            $: "jquery",
            // nie: "nie"
        }),
        //将公共代碼抽離出來合并為一個檔案
        new CommonsChunkPlugin('common.js'),
        //js檔案的壓縮
        new uglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    ]
};
           

webpack-dev-server

  • webpack-dev-server是一個輕量級的伺服器,修改檔案源碼後,自動重新整理頁面将修改同步到頁面上;
  • webpack-dev-server的他爹和他爹的朋友
    • webpack-dev-middleware:作為一個 webpack 中間件,它會開啟 watch mode 監聽檔案變更,并自動地在記憶體中快速地重新打包、提供新的 bundle,自動編譯(watch mode)+速度快(全部走記憶體)。
    • webpack-hot-middleware:
      • webpack 可以通過配置 webpack.HotModuleReplacementPlugin 插件來開啟全局的 HMR 能力;
      • 開啟後 bundle 檔案會變大一些,因為它加入了一個小型的 HMR 運作時(runtime),當你的應用在運作的時候,webpack 監聽到檔案變更并重新打包子產品時,HMR 會判斷這些子產品是否接受 update,若允許,則發信号通知應用進行熱替換。
  • webpack-dev-server是一個小型的Node.js Express伺服器,它使用webpack-dev-middleware來服務于webpack的包,除此自外,它還有一個通過Sock.js來連接配接到伺服器的微型運作時
  • webpack-dev-server是一個獨立的NPM包,你可以通過npm install webpack-dev-server來安裝它。
  • webpack-dev-server配置
var WebpackDevServer = require("webpack-dev-server");
var webpack = require("webpack");

var compiler = webpack({});
var server = new WebpackDevServer(compiler, {

  contentBase: "/path/to/directory",

  hot: true,

  historyApiFallback: false,

  compress: true,

  proxy: {
    "**": "http://localhost:9090"
  },
  setup: function(app) {},
  staticOptions: {},
  quiet: false,
  noInfo: false,
  lazy: true,
  filename: "bundle.js",
  watchOptions: {
    aggregateTimeout: ,
    poll: 
  },
  publicPath: "/assets/",
  headers: { "X-Custom-Header": "yes" },
  stats: { colors: true }
});
server.listen(, "localhost", function() {});
           

devServer配置項

  • contentBase
    • 即 SERVERROOT,如 “path.join(__dirname, “src/html”)”,後續通路 http://localhost:3333/index.html 時,SERVER 會從 src/html 下去查找 index.html 檔案。
    • 它可以是單個或多個位址的形式:(若不填寫該項,預設為項目根目錄。)
//單個
contentBase: path.join(__dirname, "public")
//多個:
contentBase: [path.join(__dirname, "public"), path.join(__dirname, "assets")]
           
  • port
    • 即監聽端口,預設為8080。
  • compress
    • 傳入一個 boolean 值,通知 SERVER 是否啟用 gzip。
  • hot
    • 傳入一個 boolean 值,通知 SERVER 是否啟用 HMR。
  • https
    • 可以傳入 true 來支援 https 通路,也支援傳入自定義的證書:
https: true
//也可以傳入一個對象,來支援自定義證書
https: {
  key: fs.readFileSync("/path/to/server.key"),
  cert: fs.readFileSync("/path/to/server.crt"),
  ca: fs.readFileSync("/path/to/ca.pem"),
}
           
  • proxy
    • 代理配置,适用場景是,除了 webpack-dev-server 的 SERVER(SERVER A) 之外,還有另一個在運作的 SERVER(SERVER B),而我們希望能通過 SERVER A 的相對路徑來通路到 SERVER B 上的東西。
devServer: {
    contentBase: path.join(__dirname, "src/html"),
    port: ,
    hot: true,
    proxy: {
        "/api": "http://localhost:5050"
    }
}
//運作 webpack-dev-server 後,若通路 http://localhost:3333/api/user,則相當于通路 http://localhost:5050/api/user。
           
  • publicPath
    • 如同 webpack-dev-middleware 的 publicPath 一樣,表示從記憶體中的哪個路徑去存放和檢索靜态檔案;
    • 不過官方文檔有一處錯誤需要堪正 —— 當沒有配置 devServer.publicPath 時,預設的 devServer.publicPath 并非根目錄,而是 output.publicPath;
    • 這也是為何咱們的例子裡壓根沒寫 devServer.publicPath,但還能正常請求到 https://localhost:3333/assets/bundle.js。
  • setup
    • webpack-dev-server 的服務應用層使用了 express,故可以通過 express app 的能力來模拟資料回包,devServer.setup 方法就是幹這事的:
devServer: {
   contentBase: path.join(__dirname, "src/html"),
   port: ,
   hot: true,
   setup(app){  //模拟資料
       app.get('/getJSON', function(req, res) {
           res.json({ name: 'vajoy' });
       });
   }
}
           

webpack插件

  • webpack 通過 plugins 實作各種功能。常見的 plugins 如下:
    • webpack.DefinePlugin 定義環境變量;
    const webpack = require('webpack'); 
    const NODE_ENV = process.env.NODE_ENV; // 從指令行環境擷取 NODE_ENV 參數 
    module.exports = { 
        plugins: [ 
            new webpack.DefinePlugin({ 
                'process.env': { 
                    'NODE_ENV': JSON.stringify(NODE_ENV) 
                    } // 定義浏覽器中的替換的變量為 `process.env.NODE_ENV` 
            })
         ] 
     }
               
    • webpack.EnvironmentPlugin 定義環境變量;
    const webpack = require('webpack');
    module.exports = {
        plugins: [
            new webpack.EnvironmentPlugin([
                'NODE_ENV'
            ])
        ]
    }
               
    • webpack.optimize.CommonsChunkPlugin 共用 js 打包
    • html-webpack-plugin 使用模版生成 html 檔案
    • webpack-visualizer-plugin 輸出依賴檔案分析圖表
    • webpack.HotModuleReplacementPlugin 代碼熱更新,用于調試模式
    • webpack.optimize.OccurrenceOrderPlugin 調整子產品的打包順序,用到次數更多的會出現在檔案的前面
    • webpack.NoErrorsPlugin 建構過程中有報錯,不認為建構完成
    • webpack.ProgressPlugin 輸出建構進度
    • webpack.BannerPlugin 在檔案頭添加注釋
    • webpack.optimize.UglifyJsPlugin 壓縮 js
    • webpack.optimize.DedupePlugin 去除重複依賴
    • extract-text-webpack-plugin 從 js 中提取出樣式檔案,單獨打包成 css 檔案;
    • clean-webpack-plugin每次運作清除指定目錄下打封包件;

extract-text-webpack-plugin

  • 作用:該插件的主要是為了抽離css樣式,防止将樣式打包在js中引起頁面樣式加載錯亂的現象;
  • 安裝:

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

  • 插件參數:
    • use:指需要什麼樣的loader去編譯檔案,這裡由于源檔案是.css是以選擇css-loader
    • fallback:編譯後用什麼loader來提取css檔案
    • publicfile:用來覆寫項目路徑,生成該css檔案的檔案路徑
  • 使用:
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader", // 編譯後用什麼loader來提取css檔案
          use: "css-loader" // 指需要什麼樣的loader去編譯檔案,這裡由于源檔案是.css是以選擇css-loader
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("styles.css"),
  ]
}
           

開發/生産環境打包

  • webpack.base.conf.js
  • 開啟source Map ,幫助調試

webpack項目中的最佳配置

  • webpack官方提供的配置方法是通過module.exports傳回一個json,但是這種場景不靈活,不能适配多種場景。
  • 比如要解決:production模式和development模式,webpack的配置是有差異的,大緻有兩種思路。
    • 1、兩份配置檔案

      webpack.config.production.js/webpack.config.development.js

      ,然後不同場景下,使用不同的配置檔案。
    • 2、通過module.exports傳回函數,該函數能接受參數。
    • 相對來說,第一種更簡單,但是重複配置多;第二種更靈活,推薦第二種方式。
//傳回函數的方式的配置代碼架子如下:
module.exports = function(env) {
    return {
        //上下文
        context: config.context,
        //入口檔案,是所有依賴關系的入口,webpack從這個入口開始靜态解析,分析子產品之間的依賴關系。
        entry: config.src,
        //打包輸出的配置
        output: {
            path: path.join(config.jsDest, project),
            filename: '[name].js',
            chunkFilename: '[name].[chunkhash:8].js',
            publicPath: '/assets/' + project + '/'
        },
        //SourceMap選項,便于開發模式下調試。
        devtool: "eval",
        //監聽模式,增量更新,開發必備!
        watch: false,
        //優化。
        profile: true,
        //webpack建構的過程中會生成很多臨時的檔案,打開cache可以讓這些臨時的檔案緩存起來,進而更快的建構。
        cache: true,
        //loaders用來對檔案做預處理。這樣webpack就可以打包任何靜态檔案。
        module: {
            loaders: getLoaders(env)
        },
        //子產品别名,這樣可以更友善的引用子產品。
        resolve: {
            alias: getAlias(env)
        },
        //webpack的一些内置功能均是以插件的形式提供。
        plugins: getPlugins(env)
    };
}
           

gitHub參考位址:https://github.com/mutouafangzi/webpackDemo20180222