這是一篇關於webpack 4手工搭建重點問題的分析,webpack 3相關能夠戳這裏: https://github.com/Eleven90/webpack-pages-V3,這裏並不試圖從零手把手去堆代碼,而是對其中的重點問題作稍微深刻一點的解讀。某些細節這裏若是沒有說起,項目代碼裏多半已解決。
這是最新的 babel 配置,和網上的諸多教程可能有不一樣,能夠自行測試驗證有效性。
基礎依賴包css
npm i babel-loader@8 @babel/core -D
從 babel7 開始,全部的官方插件和主要模塊,都放在了 @babel 的命名空間下。從而能夠避免在 npm 倉庫中 babel 相關名稱被搶注的問題。
在 package.json 同級添加.babelrc 配置文件,先空着。html
{ "presets": [], // 預設 "plugins": [] // 插件 }
package.json 文件能夠聲明須要支持到的瀏覽器版本前端
package.json 中定義(推薦)node
"browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ],
更多定義格式請查看: browserslist
.babelrc 中定義(不推薦)jquery
{ "presets": [ [ "@babel/preset-env", { "targets": { "chrome": "58", "ie": "11" } } ] ] }
@babel/preset-env
或@babel/plugin-transform-runtime
,二選一便可。使用@babel/preset-envwebpack
安裝依賴包:git
npm i @babel/preset-env @babel/polyfill -D
.babelrc 文件寫上配置,@babel/polyfill 不用寫入配置,會自動被調用。es6
{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "entry", "modules": false, } ] ] }
配置參數github
modules
參數,"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false
,默認值是 auto。 useBuiltIns
參數,"usage" | "entry" | false
,默認值是 false。web
false
:須要在 js 代碼第一行主動 import '@babel/polyfill',會將@babel/polyfill 整個包所有導入。 entry
:須要在 js 代碼第一行主動 import '@babel/polyfill',會將 browserslist 環境不支持的全部墊片都導入。 usage
:項目裏不用主動 import,會自動將代碼裏已使用到的、且 browserslist 環境不支持的墊片導入。 targets
參數,用來配置須要支持的的環境,不只支持瀏覽器,還支持 node。若是沒有配置 targets 選項,就會讀取項目中的 browserslist 配置項。loose
參數,默認值是 false,若是 preset-env 中包含的 plugin 支持 loose 的設置,那麼能夠經過這個字段來作統一的設置。使用@babel/plugin-transform-runtime
安裝依賴包
npm i @babel/plugin-transform-runtime -D
若是配置參數 corejs 未設置或爲 false,需安裝依賴@babel/runtime
(這部分代碼會被抽離並打包到應用 js 裏,因此能夠安裝在 dependencies 裏),僅對 es6 語法轉譯,而不對新 API 轉譯。
npm i @babel/runtime
若是配置參數 corejs 設置爲 2,需安裝依賴@babel/runtime-corejs2
(同上,推薦安裝在 dependencies 裏。),對語法、新 API 都轉譯。
npm i @babel/runtime-corejs2
corejs:2
,可是,檢測不到‘hello‘.includes(‘h‘)
這種句法,因此存在必定隱患,書寫代碼時需注意。.babelrc 文件寫上配置
{ "presets": [], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": 2 // 推薦 } ] ] }
corejs
,默認值是 false,只對語法進行轉換,不對新 API 進行處理;當設置爲 2 的時候,須要安裝@babel/runtime-corejs2
,這時會對 api 進行處理。helpers
,默認值是 true,用來開啓是否使用 helper 函數來重寫語法轉換的函數。useESModules
,默認值是 false,是否對文件使用 ES 的模塊語法,使用 ES 的模塊語法能夠減小文件的大小。@babel/preset-env
仍是@babel/plugin-transform-runtime
? (傳送門:babel polyfill 和 runtime 淺析)
@babel/preset-env + @babel/polyfill
能夠轉譯語法、新 API,但存在污染全局問題;@babel/plugin-transform-runtime + @babel/runtime-corejs2
,可按需導入,轉譯語法、新 API,且避免全局污染(babel7 中@babel/polyfill 是@babel/runtime-corejs2 的別名),可是檢測不到‘hello‘.includes(‘h‘)這種句法;@babel/polyfill 和@babel/runtime-corejs2 都使用了 core-js(v2)這個庫來進行 api 的處理。
core-js(v2)這個庫有兩個核心的文件夾,分別是 library 和 modules。@babel/runtime-corejs2 使用 library 這個文件夾,@babel/polyfill 使用 modules 這個文件夾。
library 和 modules 包含的文件基本相同,最大的不一樣是_export.js 這個文件:
// core-js/modules/_exports.js var global = require('./_global'); var core = require('./_core'); var hide = require('./_hide'); var redefine = require('./_redefine'); var ctx = require('./_ctx'); var PROTOTYPE = 'prototype'; var $export = function (type, name, source) { var IS_FORCED = type & $export.F; var IS_GLOBAL = type & $export.G; var IS_STATIC = type & $export.S; var IS_PROTO = type & $export.P; var IS_BIND = type & $export.B; var target = IS_GLOBAL ? global : IS_STATIC ? global[name] || (global[name] = {}) : (global[name] || {})[PROTOTYPE]; var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); var expProto = exports[PROTOTYPE] || (exports[PROTOTYPE] = {}); var key, own, out, exp; if (IS_GLOBAL) source = name; for (key in source) { // contains in native own = !IS_FORCED && target && target[key] !== undefined; // export native or passed out = (own ? target : source)[key]; // bind timers to global for call from export context exp = IS_BIND && own ? ctx(out, global) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; // extend global if (target) redefine(target, key, out, type & $export.U); // export if (exports[key] != out) hide(exports, key, exp); if (IS_PROTO && expProto[key] != out) expProto[key] = out; } }; global.core = core; // type bitmap $export.F = 1; // forced $export.G = 2; // global $export.S = 4; // static $export.P = 8; // proto $export.B = 16; // bind $export.W = 32; // wrap $export.U = 64; // safe $export.R = 128; // real proto method for `library` module.exports = $export;
// core-js/library/_exports.js var global = require('./_global'); var core = require('./_core'); var ctx = require('./_ctx'); var hide = require('./_hide'); var has = require('./_has'); var PROTOTYPE = 'prototype'; var $export = function (type, name, source) { var IS_FORCED = type & $export.F; var IS_GLOBAL = type & $export.G; var IS_STATIC = type & $export.S; var IS_PROTO = type & $export.P; var IS_BIND = type & $export.B; var IS_WRAP = type & $export.W; var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); var expProto = exports[PROTOTYPE]; var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]; var key, own, out; if (IS_GLOBAL) source = name; for (key in source) { // contains in native own = !IS_FORCED && target && target[key] !== undefined; if (own && has(exports, key)) continue; // export native or passed out = own ? target[key] : source[key]; // prevent global pollution for namespaces exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] // bind timers to global for call from export context : IS_BIND && own ? ctx(out, global) // wrap global constructors for prevent change them in library : IS_WRAP && target[key] == out ? (function (C) { var F = function (a, b, c) { if (this instanceof C) { switch (arguments.length) { case 0: return new C(); case 1: return new C(a); case 2: return new C(a, b); } return new C(a, b, c); } return C.apply(this, arguments); }; F[PROTOTYPE] = C[PROTOTYPE]; return F; // make static versions for prototype methods })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% if (IS_PROTO) { (exports.virtual || (exports.virtual = {}))[key] = out; // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out); } } }; // type bitmap $export.F = 1; // forced $export.G = 2; // global $export.S = 4; // static $export.P = 8; // proto $export.B = 16; // bind $export.W = 32; // wrap $export.U = 64; // safe $export.R = 128; // real proto method for `library` module.exports = $export;
例如對Promise的轉譯,@babel/polyfill和@babel/runtime-corejs2的轉譯方式差別以下:
var p = new Promise(); // @babel/polyfill require("core-js/modules/es6.promise"); var p = new Promise(); // @babel/runtime-corejs2 var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault"); var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise")); var a = new _promise.default();
綜合上面的分析,得出結論:
若是是本身的應用: @babel/preset-env + @babel/polyfill
useBuiltIns
設置爲entry
比較不錯。import '@babel/polyfill'
,或在webpack的入口entry中寫入模塊@babel/polyfill
,會將browserslist環境不支持的全部墊片都導入;‘hello‘.includes(‘h‘)
這種句法,足夠安全且代碼體積不是特別大,推薦使用!useBuiltIns
設置爲usage
。node_modules/
目錄,若是使用到的第三方包有個別未作好ES6轉譯,有遇到bug的可能性,而且檢測不到‘hello‘.includes(‘h‘)
這種句法。useBuiltIns
設置爲false
比較不錯。
在js代碼第一行import '@babel/polyfill'
,或在webpack的入口entry中寫入模塊@babel/polyfill
,會將@babel/polyfill整個包所有導入;
最安全,但打包體積會大一些,通常不選用。
須要安裝的所有依賴:
npm i babel-loader@8 @babel/core @babel/preset-env -D npm i @babel/polyfill
.babelrc配置文件
{ "presets": [ [ "@babel/preset-env", { "modules": false, // 推薦 "useBuiltIns": "entry", // 推薦 } ] ], "plugins": [] }
若是是開發第三方類庫: @babel/plugin-transform-runtime + @babel/runtime-corejs2
;
(或者,不作轉碼處理,提醒使用者本身作好兼容處理也能夠。)
須要安裝的所有依賴:
npm i babel-loader@8 @babel/core @babel/plugin-transform-runtime -D npm i @babel/runtime-corejs2
.babelrc配置文件
{ "presets": [], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": 2 // 推薦 } ] ] }
Babel 官方認爲,把不穩定的 stage0-3 做爲一種預設是不太合理的,@babel/preset-env
、@babel/polyfill
等只支持到stage-4
級別,所以 babel 新版本廢棄了 stage 預設,轉而讓用戶本身選擇使用哪一個 proposal 特性的插件,這將帶來更多的明確性(用戶無須理解 stage,本身選的插件,本身便能明確的知道代碼中可使用哪一個特性)。
全部建議特性的插件,都改變了命名規範,即相似 @babel/plugin-proposal-function-bind
這樣的命名方式來代表這是個 proposal 階段特性。
因此,處於建議階段的特性,基本都已從@babel/preset-env
、@babel/polyfill
等包中被移除,須要本身去另外安裝對應的 preset、plugin,(通常你能找到的名稱裏有 proposal 字樣的包,須要本身在@babel/preset-env
、@babel/plugin-transform-runtime
之外作配置)。
各個級別當前能夠選用的 proposal 插件大概以下(傳送門):
{ "plugins": [ // Stage 0 "@babel/plugin-proposal-function-bind", // Stage 1 "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-logical-assignment-operators", ["@babel/plugin-proposal-optional-chaining", { "loose": false }], ["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }], ["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }], "@babel/plugin-proposal-do-expressions", // Stage 2 ["@babel/plugin-proposal-decorators", { "legacy": true }], "@babel/plugin-proposal-function-sent", "@babel/plugin-proposal-export-namespace-from", "@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-throw-expressions", // Stage 3 "@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-import-meta", ["@babel/plugin-proposal-class-properties", { "loose": false }], "@babel/plugin-proposal-json-strings" ] }
配置裝飾器語法支持
安裝依賴
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
.babelrc 增長配置
{ "presets": [], "plugins": [ [ "@babel/plugin-proposal-decorators", // @babel/plugin-proposal-decorators須要在@babel/plugin-proposal-class-properties以前 { "legacy": true // 推薦 } ], [ "@babel/plugin-proposal-class-properties", { "loose": true // babel編譯時,對class的屬性採用賦值表達式,而不是Object.defineProperty(更簡潔) } ] ] }
配置 import 動態導入支持
安裝依賴
npm i @babel/plugin-syntax-dynamic-import -D
.babelrc 文件增長配置
{ "presets": [], "plugins": [ "@babel/plugin-syntax-dynamic-import", ] }
正常若是有多個入口,須要在 entry 中,以對象形式將全部入口都配置一遍,html 模版目錄也須要 new 不少個 HtmlWebpackPlugin 來配置對應的頁面模版,是否能夠自動掃描? 不管多少個入口,只管新建,而不用管理入口配置?能夠的!
安裝 node 模塊 glob ( 掃描文件就靠它了 ).
npm i glob -D
const glob = require('glob')
自動掃描獲取入口文件、html 模版(統一放在 utils.js 文件裏)
/** * 獲取文件 * @param {String} filesPath 文件目錄 * @returns {Object} 文件集合(文件名: 文件路徑) */ const getFiles = filesPath => { let files = glob.sync(filesPath) let obj = {} let filePath, basename, extname for (let i = 0; i < files.length; i++) { filePath = files[i] extname = path.extname(filePath) // 擴展名 eg: .html basename = path.basename(filePath, extname) // 文件名 eg: index // eg: { index: '/src/views/index/index.js' } obj[basename] = path.resolve(appDirectory, filePath) } return obj } /** * 打包入口 * 1.容許文件夾層級嵌套; * 2.入口js的名稱不容許重名; */ const entries = getFiles('src/views/**/*.js') /** * 頁面的模版 * 1.容許文件夾層級嵌套; * 2.html的名稱不容許重名; */ const templates = getFiles('src/views/**/*.html') /** * 獲取entry入口,爲了處理在某些時候,entry入口會加 polyfill等: * 1.容許文件夾層級嵌套; * 2.入口的名稱不容許重名; * * @returns {Object} entry 入口列表(對象形式) */ const getEntries = () => { let entry = {} for (let name in entries) { entry[name] = entries[name] } return entry }
webpack 打包入口
module.exports = { entry: utils.getEntries(), }
html 模版自動引入打包資源(區分 dev 和 prod 環境,配置不一樣,一樣抽離到 utils.js 文件更好一些)
/** * 生成webpack.config.dev.js的plugins下new HtmlWebpackPlugin()配置 * @returns {Array} new HtmlWebpackPlugin()列表 */ const getHtmlWebpackPluginsDev = () => { let htmlWebpackPlugins = [] let setting = null for (let name in templates) { setting = { filename: `${name}.html`, template: templates[name], inject: false, // js插入的位置,true/'head'/'body'/false } // (僅)有入口的模版自動引入資源 if (name in getEntries()) { setting.chunks = [name] setting.inject = true } htmlWebpackPlugins.push(new HtmlWebpackPlugin(setting)) setting = null } return htmlWebpackPlugins } /** * 生成webpack.config.prod.js的plugins下new HtmlWebpackPlugin()配置 * @returns {Array} new HtmlWebpackPlugin()列表 */ const getHtmlWebpackPluginsProd = () => { let htmlWebpackPlugins = [] let setting = null for (let name in templates) { setting = { filename: `${name}.html`, template: templates[name], minify: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true, }, inject: false, // js插入的位置,true/'head'/'body'/false } // (僅)有入口的模版自動引入資源 if (name in getEntries()) { setting.chunks = ['manifest', 'vendor', 'common', name] setting.inject = true } htmlWebpackPlugins.push(new HtmlWebpackPlugin(setting)) setting = null } return htmlWebpackPlugins }
須要安裝的依賴包
npm i less less-loader css-loader style-loader postcss-loader postcss-preset-env postcss-import cssnano postcss-safe-parser mini-css-extract-plugin -D
過去版本的autoprefixer、postcss-cssnext已內置在postcss-preset-env內。
配置
默認會將 css 一塊兒打包到 js 裏,藉助 mini-css-extract-plugin 將 css 分離出來並自動在生成的 html 中 link 引入(過去版本中的 extract-text-webpack-plugin 已不推薦使用)。
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
{ test: /\.(less|css)$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'], } // 在啓用dev-server時,mini-css-extract-plugin插件不能使用contenthash給文件命名 => 因此本地起dev-server服務調試時,使用style-loader // USE_HMR是自定義的環境變量,意思是是否使用了熱替換中間件 const styleLoader = process.env.USE_HMR ? 'style-loader' : MiniCssExtractPlugin.loader // 經過其餘合適的方式判斷是否爲本地調試環境也同樣,自由選擇。 const styleLoader = process.env.BUILD_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader { test: /\.(less|css)$/, use: [styleLoader, 'css-loader', 'postcss-loader', 'less-loader'], },
// 單獨使用link標籤加載css並設置路徑,相對於output配置中的publickPath new MiniCssExtractPlugin({ filename: 'static/css/[name].[contenthash:7].css', // 注意這裏使用的是contenthash,不然任意的js改動,打包時都會致使css的文件名也跟着變更。 chunkFilename: 'static/css/[name].[contenthash:7].css', })
PostCSS 自己不會對你的 CSS 作任何事情, 你須要安裝一些 plugins 才能開始工做.
參考文檔:postcss GitHub 文檔
在 package.json 同級目錄新建 postcss.config.js 文件:
module.exports = { // parser: 'sugarss', // 是一個以縮進爲基礎的語法,相似於 Sass 和 Stylus,https://github.com/postcss/sugarss plugins: { 'postcss-import': {}, 'postcss-preset-env': {}, 'cssnano': {}, 'postcss-flexbugs-fixes': {}, } }
經常使用的插件:
less 是預處理,而 PostCSS 是後處理,基本支持 less 等預處理器的功能,自動添加瀏覽器廠商前綴向前兼容,容許書寫下一代 css 語法 ,能夠在編譯時去除冗餘的 css 代碼,PostCSS 聲稱比預處理器快 3-30 倍. 由於 PostCSS,可能咱們要放棄 less/sass/stylus 了。
css 中引入的圖片( 或其它資源 ) ==> url-loader
配置了 url-loader 之後,webpack 編譯時能夠自動將小圖轉成 base64 編碼,將大圖改寫 url 並將文件生成到指定目錄下 ( file-loader 能夠完成文件生成,可是不能小圖轉 base64,因此統一用 url-loader,但 url-loader 在處理大圖的時候是自動去調用 file-loader,因此你仍然須要 install file-loader )。
// 處理圖片(file-loader來處理也能夠,url-loader更適合圖片) { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'static/assets/images/[name].[hash:7].[ext]', }, }, // 處理多媒體文件 { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'static/assets/media/[name].[hash:7].[ext]', }, }, // 處理字體文件 { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'static/assets/fonts/[name].[hash:7].[ext]' } },
html 頁面中引入的圖片( 或其它資源 ) ==> html-loader
css 中的圖片 url-loader 處理便可,而 html 中 img 標籤引入的圖片,不作工做的狀況下: 圖片將不會被處理,路徑也不會被改寫,即最終編譯完成後這部分圖片是找不到的,怎麼辦? html-loader !( 這個時候你應該是 url-loader 和 html-loader 都配置了,因此 css 中圖片、頁面引入的圖片、css 中的字體文件、頁面引入的多媒體文件等, 通通都會在編譯時被處理 )。
// html中引用的靜態資源在這裏處理,默認配置參數attrs=img:src,處理圖片的src引用的資源. { test: /\.html$/, loader: 'html-loader', options: { // 除了img的src,還能夠繼續配置處理更多html引入的資源(不能在頁面直接寫路徑,又須要webpack處理怎麼辦?先require再js寫入). attrs: ['img:src', 'img:data-src', 'audio:src'], minimize: false, removeComments: true, collapseWhitespace: false } }
有的時候, 圖片可能既不在 css 中, 也不在 html 中引入, 怎麼辦?
import img from 'xxx/xxx/123.jpg' 或 let img = require('xxx/xxx/123.jpg')
js 中引用 img,webpack 將會自動搞定它。
圖片等資源的訪問路徑問題:
通過上面的處理,靜態資源處理基本沒有問題了,webpack 編譯時將會將文件打包到你指定的生成目錄,可是不一樣位置的圖片路徑改寫會是一個問題.
所有經過絕對路徑訪問便可,在 output 下的 publicPath 填上適當的 server 端頭,來保證全部靜態資源文件路徑能被訪問到,具體要根據服務器部署的目錄結構來作修改。
output: { path: path.resolve(__dirname, 'dist'), // 輸出目錄的配置,模板、樣式、腳本、圖片等資源的路徑配置都相對於它 publicPath: '/', // 模板、樣式、腳本、圖片等資源對應的server上的路徑 }
html-webpack-plugin插件,配置:
const HtmlWebpackPlugin = require('html-webpack-plugin')
new HtmlWebpackPlugin({ favicon: './src/assets/img/favicon.ico', // favicon路徑,經過webpack引入同時能夠生成hash值 filename: './views/index.html', // 生成的html存放路徑,相對於path template: './src/views/index.html', // html模板路徑 title: '首頁', // 頁面title meta: '', // 容許插入meta標籤,如=>meta: {viewport: 'width=device-width,initial-scale=1, shrink-to-fit=no'} inject: 'body', // js插入的位置,true/'head'/'body'/false hash: true, // 爲靜態資源生成hash值(js和css) chunks: ['vendors', 'index'], // 須要在此頁面引入的chunk,不配置就會引入全部頁面的資源 minify: { // 壓縮html文件 removeComments: true, // 移除html中的註釋 collapseWhitespace: true, // 刪除空白符與換行符 }, })
script-loader 把咱們指定的模塊 JS 文件轉成純字符串, exports-loader 將須要的 js 對象 module.exports 導出, 以支持 import 或 require 導入.
安裝依賴包
npm i script-loader exports-loader -D
配置
{ test: require.resolve('zepto'), loader: 'exports-loader?window.Zepto!script-loader' }
以上是正常處理一個 _"能夠 NPM 安裝但又不符合 webpack 模塊化規範" 的庫, 例如其它庫 XX, 處理後能夠直接 import xx from XX 後使用; 可是, zepto 有點特殊, 默認 npm 安裝的包或者從 github clone 的包, 都是僅包含 5 個模塊, 其它如經常使用的 touch 模塊是未包含的, 想要正常使用還需作得更多._
a) 打包出一個包含更多須要模塊的 zepto 包
從 github clone 官方的包下來, 找到名爲 make 的文件 ( 在 package.json 同級目錄 ), 用記事本打開, 找到這一行 modules = (env['MODULES'] || 'zepto event ajax form ie').split(' ')
, 應該是在第 41 行, 手動修改加入你想要引入的模塊, 而後保存;
b) 在 make 文件同級目錄 => 右鍵打開終端或 git bash => 敲 npm i 安裝 zepto 源碼須要的 node 包 ( 這裏你應當是已經已安裝過 nodejs 了, 若是沒有, 安裝好後再作這一步 ), 等待安裝結束.
c) 在剛纔打開的 終端/git bash 敲命令 npm run-script dist, 若是沒有報錯, 你應該在這個打開的文件夾裏能夠看到生成了一個文件夾 dist, 打開它, 包含新模塊的 zepto 包就在這了, Over !
拿到新的 zepto 包後, 建議放到本身的 src 下 lib 目錄( 第三方工具包目錄 ), 再也不經過 npm 的方式去安裝和更新 zepto 了 ( 由於未來 npm update 後的 zepto 又將缺乏模塊,未來別人也會出現誤操做 ); 如今開始對這個放在 lib 目錄下的 zepto.min.js 進行處理:
a) 經過 script-loader、exports-loader 轉成符合 webpack 模塊化規範的包
{ // # require.resolve()是nodejs用來查找模塊位置的方法,返回模塊的入口文件 test: require.resolve('./src/js/lib/zepto.min.js'), loader: 'exports-loader?window.Zepto!script-loader' }
b) 給模塊配置別名
resolve: { alias: { 'zepto': path.resolve(__dirname, './src/js/lib/zepto.min.js') } }
c) 自動加載模塊, 再也不處處 import 或 require
new webpack.ProvidePlugin({ $: 'zepto', Zepto: 'zepto', })
大功告成, 如今使用 zepto 跟你使用 jquery 或其它 node 包是同樣的開發體驗了 !
以上, 演示的是對於一個第三方庫( 不能 npm 安裝,也不符合 webpack 規範 ), 如何去處理, 達到和正常 npm 安裝同樣的開發體驗, 僅就 zepto 來講, npm 庫有符合 webpack 規範的不一樣版本 ( zepto-webpack, 或 zepto-modules), 有須要能夠試試.
平時意圖使用某個包, 先去 NPM 官網搜一搜比較好.
某些時候,應用中依賴了某些模塊,但但願將這些模塊獨立經過CDN引入,以減少包的體積,因此沒必要將這些模塊打包,例如:jQuery。特定場景下,這個功能會有用武之地!
module.exports = { ... output: { ... }, externals: { jquery: "jQuery" }, ... }
一般打包js庫會選擇rollup,可是webpack一樣能夠作到,若是是須要對css、圖片等有較多應用的js庫,webpack會有更多優點,因rollup對樣式、圖片的處理能力是比較弱的。
配置
打包出全部環境均可以使用的包—— umd
module.exports = { ... entry: { sdk: 'xxxxxxx.js', }, output: { ... library: '[name]', libraryTarget: 'umd', libraryExport: 'default', umdNamedDefine: true, // 會對 UMD 的構建過程當中的 AMD 模塊進行命名,不然就使用匿名的 define }, ... }
應用導出
export default { a: xxxx, b: xxxx, c: xxxx, }
打包出的js,將支持import、requrie導入,script標籤導入,能夠經過window.sdk使用等:
// import import { a, b, c } from '........js' // require const anything = require('........js') // window window.sdk window.sdk.a // node global.sdk global.sdk.a
知識擴展:
安裝依賴包
npm i webpack-dev-server -D
經常使用配置
devServer: { contentBase: path.join(__dirname, 'static'), // # 告訴服務器從哪裏提供內容(默認當前工做目錄) host: 'localhost', // # 默認localhost,想外部可訪問用'0.0.0.0' openPage: 'views/index.html', // # 指定默認啓動瀏覽器時打開的頁面 index: 'views/index.html', // # 指定首頁位置 port: 9090, // # 默認8080 inline: true, // # 能夠監控js變化 hot: true, // # 熱啓動 open: true, // # 自動打開瀏覽器 compress: true, // # 一切服務都啓用gzip 壓縮 watchContentBase: true // # contentBase下文件變更將reload頁面(默認false) }
運行命令 ( package.json 配置命令 => npm run dev )
"dev": "cross-env BUILD_ENV=development webpack-dev-server --mode development --colors --profile"
根據目錄結構的不一樣, contentBase、openPage 參數要配置合適的值, 不然運行時應該不會馬上訪問到你的首頁; 同時要注意你的 publicPath, 靜態資源打包後生成的路徑是一個須要思考的點, 這與你的目錄結構有關。
某些時候,你可能想要build出前端代碼後,直接在本地訪問看看結果。能夠經過修改publicPath來變動靜態資源引用路徑,或者起一個本地服務來訪問。
新建 prod.server.js 文件
let express = require('express') let compression = require('compression') let app = express() let port = 9898 app.use(compression()) app.use(express.static('./static/')) module.exports = app.listen(port, function(err) { if (err) { console.log(err) return } console.log('Listening at http://localhost:' + port + '\n') })
運行命令
node prod.server.js
訪問路徑
localhost:9898/views/
比本身配置一個 express 服務更簡潔的方式,去訪問打包後的資源。
安裝依賴
npm i http-server -D
package.json 配置命令
"scripts": { "http-server": "http-server dist" }
運行命令
npm run http-server
訪問路徑
localhost:8080 或 http://127.0.0.1:8080
安裝依賴
npm i eslint eslint-loader eslint-friendly-formatter babel-eslint -D
eslint-friendly-formatter,指定終端中輸出eslint提示信息的格式。
增長配置
{ test: /\.js$/, enforce: 'pre', loader: 'eslint-loader', include: [paths.appSrc], exclude: [ /node_modules/, ], options: { formatter: require('eslint-friendly-formatter'), }, },
package.json
文件同級增長文件.eslintrc.js
module.exports = { "root": true, "parserOptions": { "sourceType": "module", }, "parser": "babel-eslint", // eslint未支持的js新特性先進行轉換 "env": { "browser": true, "es6": true, "node": true, "shared-node-browser": true, "commonjs": true, }, "globals": { // 設置全局變量(false:不容許重寫;) "BUILD_ENV": false, }, "extends": "eslint:recommended", // 使用官方推薦規則,使用其餘規則,須要先install,再指定。 "rules": { } }
配置項含義:
若是有須要跳過檢查的文件/文件夾,新建.eslintignore
文件
/node_modules
參考文檔
使用happypack來優化,多進程運行編譯,參考文檔: