webpack工程搭建

>>創建nodejs工程css

新建文件夾,npm init 生成package.jsonhtml

>>安裝webpack 和 webpack-dev-server前端

npm install --save-dev webpack@3.8.1 注意4.x版本語法有些變化node

npm install --save-dev webpack-dev-server@2.9.7 注意踩坑記錄1react

>>安裝babel轉碼es6jquery

Babel實際上是幾個模塊化的包,其核心功能位於稱爲babel-core的npm包中,webpack能夠把其不一樣的包整合在一塊兒使用,對於每個你須要的功能或拓展,你都須要安裝單獨的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。webpack

babel 6 與 bable-loader 7匹配,ios

另外解決promise等,用 babel-polyfill,bebel6對應版本爲babel-polyfill 6,解決組件按需加載編譯,用 babel-plugin-importgit

>>支持 react 開發es6

npm install --save-dev react react-dom 注意這裏是本地安裝,也能夠用全局安裝

安裝其餘可選插件:

html-webpack-plugin
clean-webpack-plugin
copy-webpack-plugin

>>配置webpack.config.js

 

踩坑記錄

1:webpack是3.x版本的,webpack-dev-server是3.x的版本,這兩個版本不兼容,能夠把webpack-dev-server降到2.x版本

 

踩坑解決辦法示例:TypeError: Cannot read property 'compile' of undefined #1334

解決思路: 優先使用 Google 引擎進行搜索關鍵詞句, 好比 webpack Cannot read property 'compile' of undefined;看可否找到相應的問題。

若是不行,不妨換一種方式再搜索,譬如:site:stackoverflow.com webpack Cannot read property 'compile' of undefined,在具體某個網站下搜索;若是仍是沒能找看法決辦法的話,能夠在各類平臺提問,好比 segmentfault。

額外補充: 對於 Google 這個工具還真是有必要先學,具體經常使用操做可參見:如何更好地運用 Chrome (Google)。假若,不可以沒有適用 Google 的環境,那麼這裏整理集結若干優質搜索引擎,堪稱 Google 搜索優質替代品,可供參考。

 

package.json 示例:

{
  "name": "webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.config.js --watch"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "antd": "^3.25.0",
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-plugin-import": "^1.12.2",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-polyfill": "^6.26.0",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "clean-webpack-plugin": "^1.0.0",
    "copy-webpack-plugin": "^4.6.0",
    "css-loader": "^3.2.0",
    "extract-text-webpack-plugin": "^3.0.2",
    "file-loader": "^4.2.0",
    "html-webpack-plugin": "^3.2.0",
    "less-loader": "^5.0.0",
    "sass-loader": "^7.3.1",
    "style-loader": "^1.0.0",
    "uglifyjs-webpack-plugin": "^1.3.0",
    "url-loader": "^2.1.0",
    "webpack": "^3.8.1",
    "webpack-bundle-analyzer": "^3.1.0",
    "webpack-dev-server": "^2.9.7"
  },
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0"
  }
}

 

webpack.config.js 示例:

const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
// const ExtractTextPlugin = require("extract-text-webpack-plugin");
// const UglifyJsPlugin  = require('uglifyjs-webpack-plugin');

module.exports = {
  // context: path.resolve(__dirname), //webpack運行時的根目錄,默認爲當前目錄

  // 配置尋找模塊的規則
  resolve: {
    modules: [ // 尋找模塊的根目錄,array 類型,默認以 node_modules 爲根目錄
      path.resolve(__dirname),
      'node_modules'
    ],
    extensions: ['.js', '.jsx', '.json', '.css', '.less', '.scss'], // 當模塊沒有後綴名時,webpack嘗試添加後查找文件,經常使用的放前面
    alias: { // 別名,用於替換import導入模塊的路徑,例如可在dev環境導入不壓縮的包,prd環境導入壓縮的包
      "react": "react/cjs/react.production.min.js", // 將import react替換爲import "react.min.js"
      "react-dom": "react-dom/cjs/react-dom.production.min.js",
      // "LP": "xxx/lp.min.js", // 將"LP"替換爲xxx/lp.min.js
      // 'only-module$': 'new-module'
      // 使用結尾符號 $ 後,只把 'only-module'結尾的路徑 映射成 'new-module',
      // 'module/path/file' 不會被映射成 'new-module/path/file'
    },
    enforceExtension: false, // 是否強制導入語句必需要寫明文件後綴
  },

  // entry 表示入口文件,注意這裏的路徑拼上resolve.modules路徑即嘗試加載入口文件的完整路徑
  // 類型能夠是 string | object | array   
  // entry: "src/js/entry.js", // 只有1個入口,入口只有1個文件,對應1個chunk出口文件,且命名爲main
  // entry: ["src/js/entry1.js", "src/js/entry2.js"], // 只有1個入口,入口有2個文件,對應1個chunk出口文件,且命名爲main
  entry: { // 有2個入口,對應2個chunk出口文件。entry爲object時對應多個chunk出口文件,名稱默認爲key值。
    // 'vendor': ['babel-polyfill', 'isomorphic-fetch', 'prop-types', 'react', 'react-dom', 'react-redux', 'react-router-dom', 'redux', 'react-bootstrap'],
    'vendor': ['babel-polyfill', 'react', 'react-dom'],
    "index": "src/app/index.js"
  },
  // entry: ()=>{ // 可由函數動態生成entry
  //   return {
  //     a:"xxx",
  //     b:"yyy"
  //   }
  // }

  // resolveLoader: { // 告訴 webpack 如何尋找Loader源文件,可用於加載本地的Loader
  //   modules: ['node_modules'], // 去哪一個目錄尋找
  //   extensions: ['.js', 'json'], // 入口文件後綴
  //   mainFields: ['loader', 'main'] // 優先使用哪一種類型的入口,可不配置
  // },

  externals: { // 告訴webpack要構建的代碼中使用了哪些不用打包的模塊(一般直接在模板html的script標籤中引入),即直接使用JS運行環境提供的全局變量
    jquery: 'window.jQuery' // 將導入語句裏的jqurey替換成全局變量 window.jQuery。例如代碼中有 import $ from 'jquery' 會被正確的替換
  }, // 通常代碼中也不會用模塊引入的語法引入自己不打包的模塊,所以這一項也能夠不配置

  // 如何輸出結果:在 Webpack 通過一系列處理後,如何輸出最終想要的代碼。
  output: {
    // 輸出文件存放的目錄,必須是 string 類型的絕對路徑。
    path: path.resolve(__dirname, 'dist'),
    // 引用資源的路徑 URL 前綴,拼上entry的路徑即爲資源訪問路徑。
    // publicPath: 'https://cdn.example.com/', // 放到 CDN 上去,html中引入js時使用https://cdn.example.com/xxx.js
    // 一般本地調試和發佈到線上時訪問路徑不一樣,能夠根據參數化選擇:
    // publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath

    // 輸出文件的名稱
    // filename: 'bundle.js', // 完整的名稱
    // filename: '[name].js', // 當配置了多個 entry 時,經過名稱模版爲不一樣的 entry 生成不一樣的文件名稱
    filename: '[name].[chunkHash:8].js', // 根據文件內容 hash 值取8位生成文件名稱,用於瀏覽器長時間緩存文件


    // 導出庫的名稱,string 類型
    // 不填它時,默認輸出格式是匿名的當即執行函數
    // library: 'MyLibrary',

    // 導出庫的類型,枚舉類型,默認是 var
    // 能夠是 umd | umd2 | commonjs2 | commonjs | amd | this | var | assign | window | global | jsonp ,
    // libraryTarget: 'umd', 

    // 附加 Chunk 的文件名稱,用於指定無入口、動態加載、webpack運行過程當中生成的chunk的名稱,一般也能夠在具體的plugin中去配置
    // chunkFilename: '[id].js',
    // chunkFilename: '[chunkhash].js'
  },

  // 配置模塊相關
  module: {
    rules: [ // 配置 Loader
      {
        test: /\.jsx?$/, // 正則匹配命中要使用該 Loader 的文件,能夠爲字符串或正則,或者數組類型
        // include: [ // 匹配範圍包含,即只會命中這裏面的文件
        //   path.resolve(__dirname, 'src')
        // ],
        exclude: /node_modules/, // 匹配範圍不包含,即排除這裏面的文件
        use: [{ // 使用哪些 Loader,有多個時從後往前執行,能夠經過options傳一些參數
          loader: "babel-loader"
        }],
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'] // 不傳參數時能夠簡寫
        // 注:style-loader做用是將css編譯到js中,而後運行時插入DOM節點的style屬性,可能致使js代碼較大,且css複用性不夠。可用ExtractTextWebpackPlugin優化。
        // use: ExtractTextPlugin.extract({
        //   fallback: "style-loader", // 編譯後用什麼loader來提取css文件並引入到html
        //   use: "css-loader"
        // })
      },
      {
        test: /\.less$/,
        use:['style-loader','css-loader', 'less-loader']
      },
      {
        test: /\.scss$/,
        use:['style-loader','css-loader', 'sass-loader']
      },
      {
        // 對小圖片或字體文件直接編碼爲base64提升加載速度,當圖片超過限制時直接使用file-loader拷貝
        test: /\.(png|jpe?g|gif|svg|eot|woff2?|ttf|pdf)$/,
        loader: 'url-loader',
        include: [path.resolve(__dirname, 'src')], 
        options: {
          limit: 10000, // 小於10kb的才編碼爲base64,建議10kb量級的小文件才編碼
          name: 'media/[name].[hash:7].[ext]'
          // 另外還能夠配置publicPath等參數,參見文末說明
        }
      },
    ],
    noParse: [ // 不用解析和處理的模塊,如jQuery等非模塊化的庫,被忽略的模塊不能包含import require define等模塊化語句
      /jquery|chartjs/  // 用正則匹配
    ],
    // noParse: (path) => {
    //   return /jquery|chartjs/.test(path);
    // }
  },

  plugins: [
    new CleanWebpackPlugin(['dist']),
    new CopyWebpackPlugin([{
      from: __dirname+'/src/resource', // 靜態資源目錄地址
      to: __dirname+'/dist/resource',
      ignore: ["test.html"]
    }]),
    // new ExtractTextPlugin({ // 提取js的css到文件,參見文末說明
    //   filename: 'css/style.[contenthash:16].css',
    // }),
    new webpack.optimize.CommonsChunkPlugin({ // 從vendor chunk中提取第三方公共模塊,而後從中提取webpack運行文件到runtime
      name: ['vendor','runtime'],
      filename: '[name].[hash:7].js',
      minChunks: 2 // 公共模塊被引用幾回時被提取出來
    }),
    // new webpack.optimize.CommonsChunkPlugin({ // 提取自定義公共模塊
    //     name: 'common',
    //     filename: '[name].[hash:7].js',
    //     chunks: ['first','second']//從first和second chunk中抽取commons chunk
    // }),
    // new UglifyJsPlugin(),
    new HtmlWebpackPlugin({
      title: "首頁", // 指定頁面標題
      favicon: "", // 指定頁面圖標
      template: __dirname + "/src/template/index.html", // html模板文件路徑
      inject: true, // true|head|body|false,當傳入 true或者 body時全部js模塊將被放置在body元素的底部,head時則會放在head元素內
      // minify:{    // 壓縮HTML文件
      //   removeComments:true,    // 移除HTML中的註釋
      //   collapseWhitespace:true    // 刪除空白符與換行符
      // },
      filename: "index.html", // 輸出html文件路徑
      chunks: [ // 向html script標籤中添加哪些chunk
        "runtime", // webpack運行文件要排在最前面優先加載
        "vendor",
        "index"        
      ],
      // excludeChunks: [ // 被排除的模塊
      //   "main"
      // ]
      chunksSortMode: 'manual', // 設置chunk插入到html的script標籤的順序, none|auto|dependency|manual|{function} 默認爲'auto,經常使用manual根據chunks的位置手動排序
    }),
  ],
  devServer: {
    contentBase: path.join(__dirname, "dist"), //本地服務器所加載的頁面所在的目錄
    publicPath: "/dist/", // 和output.publicPath做用同樣,本地調試用,會覆蓋output.publicPath的值,確保以/開頭以/結尾
    inline: true, // 實時刷新
    // hot: true, // 是否開啓模塊熱替換功能(不從新加載整個網頁的狀況下刷新模塊),默認關閉
    port: 9000, // 端口改成9000
    compress: true, // 是否開啓 gzip 壓縮
    https: false, // 是否開啓 HTTPS 模式
    // open:true // 自動打開瀏覽器
    // headers: { // response中注入一些響應頭
    //   'X-foo': '112233'
    // },
    // historyApiFallback: true, // 404時定向跳轉,一應用在HTML5 History API 的單頁應用,好比訪問不到路由時跳轉到index.html
    // 還能夠配置proxy 實現跨域請求,參見文末
  },
  // http://localhost:9000/webpack-dev-server 能夠查看運行時dev-server在內存中生成的目錄結構

  // 其餘配置
  devtool: 'source-map', // 配置 source-map 類型,方便調試時查看源碼
  profile: false, // 是否捕捉 Webpack 構建的性能信息,用於分析什麼緣由致使構建性能不佳
  cache: true, // 是否啓用緩存提高構建速度
  watch: true, // 是否開啓監聽模式,在文件變化時自動從新編譯(默認否,當使用devServer時默認開啓)
  watchOptions: { // 監聽模式選項
    // 不監聽的文件或文件夾,支持正則匹配。默認爲空
    ignored: /node_modules/,
    // 監聽到變化發生後會等待多長時間再去執行編譯,防止文件更新太快致使從新編譯頻率過高,默認爲300ms 
    aggregateTimeout: 300,
    // 檢測文件是否發生變化的間隔時長,默認每隔1000毫秒詢問一次
    poll: 1000
  },
  
  // 輸出文件性能檢查配置
  performance: { 
    hints: 'warning', // 有性能問題時輸出警告
    hints: 'error', // 有性能問題時輸出錯誤
    hints: false, // 關閉性能檢查
    maxAssetSize: 200000, // 最大文件大小 (單位 bytes)
    maxEntrypointSize: 400000, // 最大入口文件大小 (單位 bytes)
    assetFilter: function(assetFilename) { // 過濾要檢查的文件
      return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
    }
  },

  stats: { // 控制檯輸出日誌控制
    assets: true,
    colors: true,
    errors: true,
    errorDetails: true,
    hash: true,
  }
  
};


// node中的路徑
// __dirname: 老是返回被執行的 js 所在文件夾的絕對路徑
// __filename: 老是返回被執行的 js 的絕對路徑
// process.cwd(): 老是返回運行 node 命令時所在的文件夾的絕對路徑

/*
devServer proxy 實現跨域

有時候咱們使用webpack在本地啓動服務器的時候,因爲咱們使用的訪問的域名是 http://localhost:8081 這樣的,可是咱們服務端的接口是其餘的,
那麼就存在域名或端口號跨域的狀況下,可是很幸運的是 devServer有一個叫proxy配置項,能夠經過該配置來解決跨域的問題,那是由於 dev-server 使用了 http-proxy-middleware 包(瞭解該包的更多用法 )。
假如如今咱們本地訪問的域名是 http://localhost:8081, 可是我如今調用的是百度頁面中的一個接口,該接口地址是:http://news.baidu.com/widget?ajax=json&id=ad。如今咱們只須要在devServer中的proxy的配置就能夠了:
以下配置:
proxy: {
  '/api': {
    target: 'http://news.baidu.com', // 目標接口的域名
    // secure: true,  // https 的時候 使用該參數
    changeOrigin: true,  // 是否跨域
    pathRewrite: {
      '^/api' : ''  // 重寫路徑
    }
  }
}
而後咱們在main.js裏面編寫以下代碼:
import axios from 'axios';
axios.get('/api/widget?ajax=json&id=ad').then(res => {
  console.log(res);
});
在這裏請求我使用 axios 插件,其實和jquery是一個意思的。爲了方便就用了這個。
下面咱們來理解下上面配置的含義:
1. 首先是百度的接口地址是這樣的:http://news.baidu.com/widget?ajax=json&id=ad;
2. proxy 的配置項 '/api' 和 target: 'http://news.baidu.com' 的含義是,匹配請求中 /api 含有這樣的域名重定向 到 'http://news.baidu.com'來。
所以我在接口地址上 添加了前綴 '/api', 如: axios.get('/api/widget?ajax=json&id=ad'); 所以會自動補充前綴,也就是說,url: '/api/widget?ajax=json&id=ad' 等價
於 url: 'http://news.baidu.com/api/widget?ajax=json&id=ad'.
3. changeOrigin: true/false 還參數值是一個布爾值,含義是 是否須要跨域。
4. secure: true, 若是是https請求就須要改參數配置,須要ssl證書吧。
5. pathRewrite: {'^/api' : ''}的含義是重寫url地址,把url的地址裏面含有 '/api' 這樣的 替換成 '', 
所以接口地址就變成了 http://news.baidu.com/widget?ajax=json&id=ad; 所以就能夠請求獲得了,最後就返回
接口數據了。
*/

/* https://segmentfault.com/a/1190000018987483?utm_source=tag-newest
url-loader 會將引入的圖片(還有字體、音視頻等)編碼,生成dataURl並將其打包到文件中,引入這個dataURL就能訪問。若是圖片較大,編碼會消耗性能。
所以url-loader提供了一個limit參數,小於limit字節的文件會被轉爲DataURl,大於limit的內部調用file-loader進行copy。
接下來摘取幾個重要的屬性作說明

outputPath
該屬性指明咱們最終導出的文件路徑
最終導出的文件路徑 === output.path + url-loader.outputPath + url-loader.name

publicPath(經常使用於生成環境)
該屬性指明咱們最終引用的文件路徑(打包生成的index.html文件裏面引用資源的前綴)
最終引用的文件路徑前綴 === output.publicPath + url-loader.publicPath + url-loader.name

name
該屬性指明文件的最終名稱。
一樣的,咱們能夠直接把outputPath的內容寫到name中,同樣能夠生成對應的路徑
通過上面的說明,咱們得出結論,最終的靜態文件路徑(圖片,音頻,視頻,字體等)=== output.publicPath + url-loader.publicPath + output.path + url-loader.outputPath + url-loader.name

咱們在本地開發的時候都是localhost:8080/下面的根目錄,因此當圖片生成以下的絕對地址是不會出問題的,但是一樣的webpack配置放到生成環境上就不必定了。
由於生成環境大部分的前端靜態文件都不是在根目錄啊,有可能就是這樣的目錄結構
www/
 +folder/
   +static/
     +css/
     +img/
     +js/
   +index.html
這樣你開發環境的絕對路徑放到服務器上面天然就404了,因此要否則用相對路徑,要否則就統一寫死絕對路徑(針對不一樣環境添加不一樣的publicPath)
(一樣道理,解釋爲何css裏面的背景圖路徑404,可是這個解決起來須要用到extract-text-webpack-plugin或者mini-css-extract-plugin這個插件,前者用於webpack4如下版本,後者是4以上版本,配置options裏面的publicPath)

css 提取插件,`extract-text-webpack-plugin` 插件 或者 `mini-css-extract-plugin` 的 loader也都提供了publicPath,用來複寫提取出來的css文件中的資源的引入路徑
*/

 

 

模板html示例:

<!DOCTYPE html>
<head>
    <title><%=htmlWebpackPlugin.options.title %></title>
    <meta charset="utf-8" />
    <style>
        /* 垂直居中顯示 */
        .center-in-center{
            position: absolute;
            margin: 10px;
            padding: 10px;
            top: 50%;
            left: 50%;
            -webkit-transform: translate(-50%, -50%);
            -moz-transform: translate(-50%, -50%);
            -ms-transform: translate(-50%, -50%);
            -o-transform: translate(-50%, -50%);
            transform: translate(-50%, -50%);
        }
    </style>
</head>
<body>
    <div id="root" >
    </div>
</body>
</html>

解釋下版本號:

1.15.2對應就是MAJOR,MINOR.PATCH:1是marjor version;15是minor version;2是patch version。
MAJOR:這個版本號變化了表示有了一個不能夠和上個版本兼容的大更改。
MINOR:這個版本號變化了表示有了增長了新的功能,而且能夠向後兼容。
PATCH:這個版本號變化了表示修復了bug,而且能夠向後兼容。

當咱們使用最新的Node運行‘npm instal --save xxx',的時候,他會優先考慮使用插入符號(^)而不是波浪符號(~)了。

波浪符號(~):他會更新到當前minor version(也就是中間的那位數字)中最新的版本。放到咱們的例子中就是:body-parser:~1.15.2,這個庫會去匹配更新到1.15.x的最新版本,若是出了一個新的版本爲1.16.0,則不會自動升級。波浪符號是曾經npm安裝時候的默認符號,如今已經變爲了插入符號。

插入符號(^):這個符號就顯得很是的靈活了,他將會把當前庫的版本更新到當前major version(也就是第一位數字)中最新的版本。放到咱們的例子中就是:bluebird:^3.3.4,這個庫會去匹配3.x.x中最新的版本,可是他不會自動更新到4.0.0。

由於major version變化表示可能會影響以前版本的兼容性,因此不管是波浪符號仍是插入符號都不會自動去修改major version,由於這可能致使程序crush,可能須要手動修改代碼。

 

const  path =  require( "path");
const  webpack =  require( "webpack");
const  HtmlWebpackPlugin =  require( "html-webpack-plugin");
const  CopyWebpackPlugin =  require( "copy-webpack-plugin");
const  CleanWebpackPlugin =  require( "clean-webpack-plugin");
// const ExtractTextPlugin = require("extract-text-webpack-plugin");
// const UglifyJsPlugin  = require('uglifyjs-webpack-plugin');

module. exports = {
   // context: path.resolve(__dirname), //webpack運行時的根目錄,默認爲當前目錄

   // 配置尋找模塊的規則
   resolve: {
     modules: [  // 尋找模塊的根目錄,array 類型,默認以 node_modules 爲根目錄
       path. resolve( __dirname),
       'node_modules'
    ],
     extensions: [ '.js''.jsx''.json''.css''.less''.scss'],  // 當模塊沒有後綴名時,webpack嘗試添加後查找文件,經常使用的放前面
     alias: {  // 別名,用於替換import導入模塊的路徑,例如可在dev環境導入不壓縮的包,prd環境導入壓縮的包
       "react" :  "react/cjs/react.production.min.js"// 將import react替換爲import "react.min.js"
       "react-dom" :  "react-dom/cjs/react-dom.production.min.js",
       // "LP": "xxx/lp.min.js", // 將"LP"替換爲xxx/lp.min.js
       // 'only-module$': 'new-module'
       // 使用結尾符號 $ 後,只把 'only-module'結尾的路徑 映射成 'new-module',
       // 'module/path/file' 不會被映射成 'new-module/path/file'
    },
     enforceExtension:  false// 是否強制導入語句必需要寫明文件後綴
  },

   // entry 表示入口文件,注意這裏的路徑拼上resolve.modules路徑即嘗試加載入口文件的完整路徑
   // 類型能夠是 string | object | array   
   // entry: "src/js/entry.js", // 只有1個入口,入口只有1個文件,對應1個chunk出口文件,且命名爲main
   // entry: ["src/js/entry1.js", "src/js/entry2.js"], // 只有1個入口,入口有2個文件,對應1個chunk出口文件,且命名爲main
   entry: {  // 有2個入口,對應2個chunk出口文件。entry爲object時對應多個chunk出口文件,名稱默認爲key值。
     // 'vendor': ['babel-polyfill', 'isomorphic-fetch', 'prop-types', 'react', 'react-dom', 'react-redux', 'react-router-dom', 'redux', 'react-bootstrap'],
     'vendor' : [ 'babel-polyfill''react''react-dom'],
     "index" :  "src/app/index.js"
  },
   // entry: ()=>{ // 可由函數動態生成entry
   //   return {
   //     a:"xxx",
   //     b:"yyy"
   //   }
   // }

   // resolveLoader: { // 告訴 webpack 如何尋找Loader源文件,可用於加載本地的Loader
   //   modules: ['node_modules'], // 去哪一個目錄尋找
   //   extensions: ['.js', 'json'], // 入口文件後綴
   //   mainFields: ['loader', 'main'] // 優先使用哪一種類型的入口,可不配置
   // },

   externals: {  // 告訴webpack要構建的代碼中使用了哪些不用打包的模塊(一般直接在模板html的script標籤中引入),即直接使用JS運行環境提供的全局變量
     jquery:  'window.jQuery'  // 將導入語句裏的jqurey替換成全局變量 window.jQuery。例如代碼中有 import $ from 'jquery' 會被正確的替換
  },  // 通常代碼中也不會用模塊引入的語法引入自己不打包的模塊,所以這一項也能夠不配置

   // 如何輸出結果:在 Webpack 通過一系列處理後,如何輸出最終想要的代碼。
   output: {
     // 輸出文件存放的目錄,必須是 string 類型的絕對路徑。
     path:  path. resolve( __dirname'dist'),
     // 引用資源的路徑 URL 前綴,拼上entry的路徑即爲資源訪問路徑。
     // publicPath: 'https://cdn.example.com/', // 放到 CDN 上去,html中引入js時使用https://cdn.example.com/xxx.js
     // 一般本地調試和發佈到線上時訪問路徑不一樣,能夠根據參數化選擇:
     // publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath

     // 輸出文件的名稱
     // filename: 'bundle.js', // 完整的名稱
     // filename: '[name].js', // 當配置了多個 entry 時,經過名稱模版爲不一樣的 entry 生成不一樣的文件名稱
     filename:  '[name].[chunkHash:8].js'// 根據文件內容 hash 值取8位生成文件名稱,用於瀏覽器長時間緩存文件


     // 導出庫的名稱,string 類型
     // 不填它時,默認輸出格式是匿名的當即執行函數
     // library: 'MyLibrary',

     // 導出庫的類型,枚舉類型,默認是 var
     // 能夠是 umd | umd2 | commonjs2 | commonjs | amd | this | var | assign | window | global | jsonp ,
     // libraryTarget: 'umd', 

     // 附加 Chunk 的文件名稱,用於指定無入口、動態加載、webpack運行過程當中生成的chunk的名稱,一般也能夠在具體的plugin中去配置
     // chunkFilename: '[id].js',
     // chunkFilename: '[chunkhash].js'
  },

   // 配置模塊相關
   module: {
     rules: [  // 配置 Loader
      {
         test:  / \. jsx ? $ /// 正則匹配命中要使用該 Loader 的文件,能夠爲字符串或正則,或者數組類型
         // include: [ // 匹配範圍包含,即只會命中這裏面的文件
         //   path.resolve(__dirname, 'src')
         // ],
         exclude:  /node_modules/// 匹配範圍不包含,即排除這裏面的文件
         use: [{  // 使用哪些 Loader,有多個時從後往前執行,能夠經過options傳一些參數
           loader:  "babel-loader"
        }],
      },
      {
         test:  / \. css $ /,
         use: [ 'style-loader''css-loader'// 不傳參數時能夠簡寫
         // 注:style-loader做用是將css編譯到js中,而後運行時插入DOM節點的style屬性,可能致使js代碼較大,且css複用性不夠。可用ExtractTextWebpackPlugin優化。
         // use: ExtractTextPlugin.extract({
         //   fallback: "style-loader", // 編譯後用什麼loader來提取css文件並引入到html
         //   use: "css-loader"
         // })
      },
      {
         test:  / \. less $ /,
         use:[ 'style-loader', 'css-loader''less-loader']
      },
      {
         test:  / \. scss $ /,
         use:[ 'style-loader', 'css-loader''sass-loader']
      },
      {
         // 對小圖片或字體文件直接編碼爲base64提升加載速度,當圖片超過限制時直接使用file-loader拷貝
         test:  / \. ( png | jpe ? g | gif | svg | eot | woff2 ? | ttf | pdf ) $ /,
         loader:  'url-loader',
         include: [ path. resolve( __dirname'src')], 
         options: {
           limit:  10000// 小於10kb的才編碼爲base64,建議10kb量級的小文件才編碼
           name:  'media/[name].[hash:7].[ext]'
           // 另外還能夠配置publicPath等參數,參見文末說明
        }
      },
    ],
     noParse: [  // 不用解析和處理的模塊,如jQuery等非模塊化的庫,被忽略的模塊不能包含import require define等模塊化語句
       /jquery | chartjs/   // 用正則匹配
    ],
     // noParse: (path) => {
     //   return /jquery|chartjs/.test(path);
     // }
  },

   plugins: [
     new  CleanWebpackPlugin([ 'dist']),
     new  CopyWebpackPlugin([{
       from:  __dirname+ '/src/resource'// 靜態資源目錄地址
       to:  __dirname+ '/dist/resource',
       ignore: [ "test.html"]
    }]),
     // new ExtractTextPlugin({ // 提取js的css到文件,參見文末說明
     //   filename: 'css/style.[contenthash:16].css',
     // }),
     new  webpack. optimize. CommonsChunkPlugin({  // 從vendor chunk中提取第三方公共模塊,而後從中提取webpack運行文件到runtime
       name: [ 'vendor', 'runtime'],
       filename:  '[name].[hash:7].js',
       minChunks:  2  // 公共模塊被引用幾回時被提取出來
    }),
     // new webpack.optimize.CommonsChunkPlugin({ // 提取自定義公共模塊
     //     name: 'common',
     //     filename: '[name].[hash:7].js',
     //     chunks: ['first','second']//從first和second chunk中抽取commons chunk
     // }),
     // new UglifyJsPlugin(),
     new  HtmlWebpackPlugin({
       title:  "首頁"// 指定頁面標題
       favicon:  ""// 指定頁面圖標
       template:  __dirname +  "/src/template/index.html"// html模板文件路徑
       inject:  true// true|head|body|false,當傳入 true或者 body時全部js模塊將被放置在body元素的底部,head時則會放在head元素內
       // minify:{    // 壓縮HTML文件
       //   removeComments:true,    // 移除HTML中的註釋
       //   collapseWhitespace:true    // 刪除空白符與換行符
       // },
       filename:  "index.html"// 輸出html文件路徑
       chunks: [  // 向html script標籤中添加哪些chunk
         "runtime"// webpack運行文件要排在最前面優先加載
         "vendor",
         "index"        
      ],
       // excludeChunks: [ // 被排除的模塊
       //   "main"
       // ]
       chunksSortMode:  'manual'// 設置chunk插入到html的script標籤的順序, none|auto|dependency|manual|{function} 默認爲'auto,經常使用manual根據chunks的位置手動排序
    }),
  ],
   devServer: {
     contentBase:  path. join( __dirname"dist"),  //本地服務器所加載的頁面所在的目錄
     publicPath:  "/dist/"// 和output.publicPath做用同樣,本地調試用,會覆蓋output.publicPath的值,確保以/開頭以/結尾
     inline:  true// 實時刷新
     // hot: true, // 是否開啓模塊熱替換功能(不從新加載整個網頁的狀況下刷新模塊),默認關閉
     port:  9000// 端口改成9000
     compress:  true// 是否開啓 gzip 壓縮
     https:  false// 是否開啓 HTTPS 模式
     // open:true // 自動打開瀏覽器
     // headers: { // response中注入一些響應頭
     //   'X-foo': '112233'
     // },
     // historyApiFallback: true, // 404時定向跳轉,一應用在HTML5 History API 的單頁應用,好比訪問不到路由時跳轉到index.html
     // 還能夠配置proxy 實現跨域請求,參見文末
  },
   // http://localhost:9000/webpack-dev-server 能夠查看運行時dev-server在內存中生成的目錄結構

   // 其餘配置
   devtool:  'source-map'// 配置 source-map 類型,方便調試時查看源碼
   profile:  false// 是否捕捉 Webpack 構建的性能信息,用於分析什麼緣由致使構建性能不佳
   cache:  false// 是否啓用緩存提高構建速度
   watch:  true// 是否開啓監聽模式,在文件變化時自動從新編譯(默認否,當使用devServer時默認開啓)
   watchOptions: {  // 監聽模式選項
     // 不監聽的文件或文件夾,支持正則匹配。默認爲空
     ignored:  /node_modules/,
     // 監聽到變化發生後會等待多長時間再去執行編譯,防止文件更新太快致使從新編譯頻率過高,默認爲300ms 
     aggregateTimeout:  300,
     // 檢測文件是否發生變化的間隔時長,默認每隔1000毫秒詢問一次
     poll:  1000
  },
  
   // 輸出文件性能檢查配置
   performance: { 
     hints:  'warning'// 有性能問題時輸出警告
     hints:  'error'// 有性能問題時輸出錯誤
     hints:  false// 關閉性能檢查
     maxAssetSize:  200000// 最大文件大小 (單位 bytes)
     maxEntrypointSize:  400000// 最大入口文件大小 (單位 bytes)
     assetFilter :  function( assetFilename) {  // 過濾要檢查的文件
       return  assetFilename. endsWith( '.css') ||  assetFilename. endsWith( '.js');
    }
  },

   stats: {  // 控制檯輸出日誌控制
     assets:  true,
     colors:  true,
     errors:  true,
     errorDetails:  true,
     hash:  true,
  }
  
};


// node中的路徑
// __dirname: 老是返回被執行的 js 所在文件夾的絕對路徑
// __filename: 老是返回被執行的 js 的絕對路徑
// process.cwd(): 老是返回運行 node 命令時所在的文件夾的絕對路徑

/*
devServer proxy 實現跨域

有時候咱們使用webpack在本地啓動服務器的時候,因爲咱們使用的訪問的域名是 http://localhost:8081 這樣的,可是咱們服務端的接口是其餘的,
那麼就存在域名或端口號跨域的狀況下,可是很幸運的是 devServer有一個叫proxy配置項,能夠經過該配置來解決跨域的問題,那是由於 dev-server 使用了 http-proxy-middleware 包(瞭解該包的更多用法 )。
假如如今咱們本地訪問的域名是 http://localhost:8081, 可是我如今調用的是百度頁面中的一個接口,該接口地址是:http://news.baidu.com/widget?ajax=json&id=ad。如今咱們只須要在devServer中的proxy的配置就能夠了:
以下配置:
proxy: {
  '/api': {
    target: 'http://news.baidu.com', // 目標接口的域名
    // secure: true,  // https 的時候 使用該參數
    changeOrigin: true,  // 是否跨域
    pathRewrite: {
      '^/api' : ''  // 重寫路徑
    }
  }
}
而後咱們在main.js裏面編寫以下代碼:
import axios from 'axios';
axios.get('/api/widget?ajax=json&id=ad').then(res => {
  console.log(res);
});
在這裏請求我使用 axios 插件,其實和jquery是一個意思的。爲了方便就用了這個。
下面咱們來理解下上面配置的含義:
1. 首先是百度的接口地址是這樣的:http://news.baidu.com/widget?ajax=json&id=ad;
2. proxy 的配置項 '/api' 和 target: 'http://news.baidu.com' 的含義是,匹配請求中 /api 含有這樣的域名重定向 到 'http://news.baidu.com'來。
所以我在接口地址上 添加了前綴 '/api', 如: axios.get('/api/widget?ajax=json&id=ad'); 所以會自動補充前綴,也就是說,url: '/api/widget?ajax=json&id=ad' 等價
於 url: 'http://news.baidu.com/api/widget?ajax=json&id=ad'.
3. changeOrigin: true/false 還參數值是一個布爾值,含義是 是否須要跨域。
4. secure: true, 若是是https請求就須要改參數配置,須要ssl證書吧。
5. pathRewrite: {'^/api' : ''}的含義是重寫url地址,把url的地址裏面含有 '/api' 這樣的 替換成 '', 
所以接口地址就變成了 http://news.baidu.com/widget?ajax=json&id=ad; 所以就能夠請求獲得了,最後就返回
接口數據了。
*/

/* https://segmentfault.com/a/1190000018987483?utm_source=tag-newest
url-loader 會將引入的圖片(還有字體、音視頻等)編碼,生成dataURl並將其打包到文件中,引入這個dataURL就能訪問。若是圖片較大,編碼會消耗性能。
所以url-loader提供了一個limit參數,小於limit字節的文件會被轉爲DataURl,大於limit的內部調用file-loader進行copy。
接下來摘取幾個重要的屬性作說明

outputPath
該屬性指明咱們最終導出的文件路徑
最終導出的文件路徑 === output.path + url-loader.outputPath + url-loader.name

publicPath(經常使用於生成環境)
該屬性指明咱們最終引用的文件路徑(打包生成的index.html文件裏面引用資源的前綴)
最終引用的文件路徑前綴 === output.publicPath + url-loader.publicPath + url-loader.name

name
該屬性指明文件的最終名稱。
一樣的,咱們能夠直接把outputPath的內容寫到name中,同樣能夠生成對應的路徑
通過上面的說明,咱們得出結論,最終的靜態文件路徑(圖片,音頻,視頻,字體等)=== output.publicPath + url-loader.publicPath + output.path + url-loader.outputPath + url-loader.name

咱們在本地開發的時候都是localhost:8080/下面的根目錄,因此當圖片生成以下的絕對地址是不會出問題的,但是一樣的webpack配置放到生成環境上就不必定了。
由於生成環境大部分的前端靜態文件都不是在根目錄啊,有可能就是這樣的目錄結構
www/
 +folder/
   +static/
     +css/
     +img/
     +js/
   +index.html
這樣你開發環境的絕對路徑放到服務器上面天然就404了,因此要否則用相對路徑,要否則就統一寫死絕對路徑(針對不一樣環境添加不一樣的publicPath)
(一樣道理,解釋爲何css裏面的背景圖路徑404,可是這個解決起來須要用到extract-text-webpack-plugin或者mini-css-extract-plugin這個插件,前者用於webpack4如下版本,後者是4以上版本,配置options裏面的publicPath)

css 提取插件,`extract-text-webpack-plugin` 插件 或者 `mini-css-extract-plugin` 的 loader也都提供了publicPath,用來複寫提取出來的css文件中的資源的引入路徑
*/
相關文章
相關標籤/搜索