Reference: https://segmentfault.com/a/1190000006178770
// 一个常见的'webpack'配置文件
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text=webpack-plugin');
module.exports = {
entry: __dirname + "/app/main.js", // 已多次提及的唯一入口文件
output: {
path: __dirname + "/build",
filename: "bundle-[hash].js"
},
devtool: 'none',
devServer: {
contentBase: "./public",
historyApiFallback: true,
inline: true,
hot: true
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
}, {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader",
options: {
modules: true,
localIdentName: '[name]__[local]--[hash:base64.5]'
}
}, {
loader: "postcss-loader"
}],
})
}]
},
plugin: [
new webpack.BannerPlugin('版权所有,翻版必究'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin("style.css")
]
};
什么是Webpack,为什么要使用它?
为什么要使用Webpack
-
模块化,
-
类似于TypeScript这种在JavaScript基础上拓展的开发语言:使我们能够实现目前版本的Javascript不能直接使用的特性,并且之后还能转换为JavaScript文件使浏览器可以识别
-
Scss、less等CSS预处理器
什么是Webpack
Webpack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其他的一些浏览器不能直接运行的拓展语言(Scss、TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
Webpack和Grunt以及Gulp相比有什么特性
其实Webpack和另外两个并没有太多的可比性,Gulp/Grunt是一种能够优化前端的开发流程的工具,而Webpack是一种模块化的解决方案,不过Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具。
Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译、组合、压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。
【图片1】
Webpack的工作方式是:把你的项目当作一个整体,通过一个给定的主文件(如index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loader处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。
【图片2】
如果实在要将二者进行比较,Webpack的处理速度更快更直接,能打包更多不同类型的文件。
开始使用Webpack
安装
Webpack可以使用npm安装,新建一个空文件夹(测试文件夹),在command line/terminal 转到该文件夹后执行安装指令。
// 全局安装
npm install -g webpack
// 安装到项目目录
npm install --save-dev webpack
正式使用Webpack前的准备
1. 在上述测试文件夹中创建一个package.json文件。在command line/terminal使用npm init 命令可以自动创建这个package.json文件(需要提供项目信息);或者使用npm init -y创建package.json(默认配置信息)
2. package.json文件就绪后,我们在本项目中安装webpack作为依赖项: npm install --save-dev webpack
3. 回到测试文件夹,并在里面创建两个文件夹:src和public。其中,src文件夹用来保存原始数据和我们将写的JavaScript模块;public文件夹用来存放之后浏览器读取的文件(包括使用webpack打包生成的js文件以及一个index.html文件)。接着再创建三个文件:
?????default input js file was src/index.js, and output js file was dist/main.js?????
- index.html -- 放在public文件夹中
- Greeter.js -- 放在src文件夹中
- main.js(index.js) -- 放在src文件夹中
我们在index.html文件中写入最基础的html代码,它在这里目的在于引入打包后的js文件(把之后打包后的js文件命名为bundle.js)
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' >
<title>Webpack Sample Project</title>
</head>
<body>
<div id='root'></div>
<!-- <script src='bundle.js'></script> -->
<script src='main.js'></script>
</body>
</html>
Greeter.js中定义一个返回包含问候信息的html元素的函数,并依据CommonJS规范导出这个函数为一个模块:
// Greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings !!!";
return greet;
}
main.js (index.js)文件中写入下述代码,用以把Greeter模块返回的节点插入页面
// main.js (index.js)
const greeter = require('./Greeter.js');
document.getElementById('root').appendChild(greeter());
正式使用Webpack
webpack可以在终端使用,在基本的使用方法中:
# { extry file } 处填写入口文件的路径,本文中就是上述main.js的路径。
# { destinition for bundled file } 处填写打包文件的存放路径
# 填写路径的时候不用添加 {}
webpack {entry file} {destination for bundled file}
指定入口文件后,webpack将自动识别项目所依赖的其他文件,不过需要注意的是如果你的webpack不是全局安装的,那么当你在终端中使用命令时,需要额外指定其在node_modules中的地址!!在终端输入如下命令:
# webpack非全局安装的情况
node_modules/.bin/webpack src/main.js public/bundle.js
# node_modules/.bin/webpack src/index.js public/bundle.js
NOTE: 在最新版的NodeJS下,需要使用下面命令:
node_modules/.bin.webpack src/main.js --o public/bundle.js
# node_modules/.bin.webpack src/index.js --o public/bundle.js
NOTE: 运行webpack打包命令运行结果最好是全部通过,解决掉所有的error和warning
https://blog.youkuaiyun.com/lplife/article/details/80650993
打开bundle.js文件看到如下内容,并没有生成我们打包生成的main.js(index.js)的信息
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){const r=n(1);document.getElementById("root").appendChild(r())},function(e,t){e.exports=function(){var e=document.createElement("div");return e.textContent="Hi there and greetings !!!",e}}]);
黄色部分的警告是: 没有设置模式,有开发模式和生产模式两种。添加上"dev"和"build"两个字段到package.json文件中
NOTE:这种模式会默认找当前目录下src\index.js,并且会打包到当前目录下dist下的main.js
"script": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack --mode development",
"build": "webpack --mode production"
},
此时,打包后的js文件如下:
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./src/index.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./src/Greeter.js":
/*!************************!*\
!*** ./src/Greeter.js ***!
\************************/
/*! no static exports found */
/***/ (function(module, exports) {
eval("module.exports = function() {\r\n var greet = document.createElement('div');\r\n greet.textContent = 'Hi there and greetings !!!';\r\n return greet;\r\n}\n\n//# sourceURL=webpack:///./src/Greeter.js?");
/***/ }),
/***/ "./src/index.js":
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
eval("const greeter = __webpack_require__(/*! ./Greeter.js */ \"./src/Greeter.js\");\r\ndocument.getElementById('root').appendChild(greeter());\n\n//# sourceURL=webpack:///./src/index.js?");
/***/ })
/******/ });
通过配置文件来使用webpack
webpack拥有很多其他的比较高级的功能(如loaders和plugins),这些功能其实都可以通过命令行模式实现,但是这样不太方便而且容易出错,更好的办法是定义一个配置文件,这个配置文件其实也是一个简单的JavaScript模块,我们可以把所有与打包相关的信息都放在里面
继续上面的例子来说明如何操作配置文件,在测试文件夹下新建一个webpack.config.js的文件,我们在其中写入如下简单的配置项,目前的配置主要涉及到的内容是入口文件路径和打包后文件的存放路径。
module.exports = {
entry: __dirname + "/src/main.js", // 已多次提及的唯一入口文件
output: {
path: __dirname + "/public", // 打包后输出文件的存放路径
filename: "bundle.js" // 打包后输出文件名
}
}
// "__dirname" 是node.js中的一个全部变量,它指向当前执行脚本所在的目录。
有了这个配置之后,再打包文件,只需再终端运行webpack(注意非全局安装时需要指定路径)命令就可以了,这条命令会自动引用webpack.config.js文件中的配置选项。
又学会了一种使用webpack的方法,这种方法不用管那些烦人的命令行参数。 解析来我们讨论不使用node_moduls\.bin\webpack命令来
更快捷的执行打包任务
在命令行中输入命令需要代码类似于node_modules\.bin\webpack这样的路径很容易犯错,不过,npm可以引导任务执行,对npm进来配置后可以在命令行中使用简单的npm start命令来代替上面略微繁琐的命令。在package.json中对scripts对象进行相关设置即可,设置方法如下:
{
"name": "webpack-sample-project",
"version": "1.0.0",
"description": "Sample webpack project",
"scripts": {
"start": "webpack"
},
"author": "No One",
"license": "ISC",
"devDependencies": {
"webpack": "3.10.0"
}
}
NOTE: package.json中的script会按照一定顺序寻找命令对应位置,本地的node_module\.bin路径就在这个寻找清单中,所以无论是全局还是全局安装的webpack,都不需要写前面那些指明详细的路径了。
npm的start命令是一个特殊的脚本名称,其特殊性表现在,在命令行中使用npm start就可以执行其对应的命令,如果对应的脚本名称不是strat,想要在命令行中运行时,需要这样使用npm run {script name} 如 npm run build,我们在命令行中输入npm start试试。
webpack的强大功能
生成Source Maps(使调试更容易)
在webpack的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:
- source-map: 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包速度
- cheap-module-source-map: 在一个单独的文件中生成一个不带列映射的map,不带列映射提高了打包速度,但是也使debug工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便
- eval-source-map: 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的source map,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段是一个非常好的选项,在生产阶段一定不要启用这个选项
- cheap-module-eval-source-map: 在打包文件时最快的生成source map的方法,生成的source map会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点
正如以上所说,上述选项由上到下打包速度越来越快,不过同时也具有越来越多的负面作用,较快的打包速度的后果就是对打包后的文件执行有一定的影响。
对小到中型项目,eval-source-map是一个很好的选项,再次强调你只应该在开发阶段使用它!!!
我们继续对上文新建的webpack.config.js,进行如下配置:
module.exports = {
devtool: "eval-source-map",
entry: __dirname + "/src/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
}
}
NOTE: cheap-module-eval-source-map方法构建速度更快,但是不利于调试开发,推荐在大型项目考虑时间成本时使用。
使用webpack构建本地服务器
webpack提供一个可选的本地开发服务器(浏览器监听代码的修改,并自动刷新显示修改后的结果),这个本地服务器基于node.js构建,可以实现实时监控代码的修改,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖项:
npm install --save-dev webpack-dev-server
devserver作为webpack配置选项中的一项,以下是它的一些配置选项,更多配置参考https://webpack.js.org/configuration/dev-server/
- contentBase: 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例子设置到”public“目录)
- port: 设置默认监听端口,如果省略,默认为”8080“
- inline: 设置为”true“,当源文件改变时会自动刷新页面
- historyApiFallback: 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
把这些命令加到webpack的配置文件webpack.config.js中,现在的配置文件如下:
module.exports = {
devtool: "eval-source-map",
entry: __dirname + "/src/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
},
devServer: {
contentBase: "./public",
historyApiFallback: true,
inline: true
}
}
在package.json中的script对象中添加如下命令,用以开启本地服务器:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack",
"server": "webpack-dev-server --open"
}
在终端中输入npm run server即可在本地的8080端口查看结果。
----------------------------------------------------------------------------------------------------------------------------------
run your local server by one of following:
1. [path]\webpack-dev-server --content-base [Path_That_Contains_html_File] --> pure command line.
2. if webpack.config.js exists, you can run webpack-dev-serve --open
3. Add an attribute named 'server' in package.json, the value was 'webpack-dev-server --open', then you can run 'npm run server'
----------------------------------------------------------------------------------------------------------------------------------