本文大多整理于入门Webpack,看这篇就够了以及webpack源码分析,本文以两篇文章为基础,做了一定整理
什么是webpack?
如果你看到这篇文章,那你八成知道了它的主要功能——打包器
但是
为什么要打包呢?
- 压缩,打包后,文件会明细的减少体积
- 模块整合,把你五花八门的应用整合为依赖关系图
不止打包
除了打包这一主要功能,webpack还衍生出了其他功能,比如
- 其他语言的转译,比如less
- 特定写法的转译,比如Es6
- 一切皆模块,css的font和图片都可以被正确的处理
- 本地服务器,包括热加载
- 强大的插件,可以达成很多的特别的需求,比如js文件中嵌入版权声明
或许webpack马上就过时了?
HTTP2协议马上就要到应用阶段了,届时,请求更快,请求上限的锁也会被解除,性能提升的情况下,webpack还是必要的吗?我认为它仍是必要的,毕竟就算HTTP2被应用了,服务器和客户端的性能并不会在短时间有质变。一个合理的整合工具还是必要的。就像这位同仁的见解:
What is the value of using Webpack with HTTP/2
以上观点,欢迎评论讨论
那么现在
开始
安装
//全局安装
npm install -g webpack
//安装到你的项目目录
npm install --save-dev webpack
简单配置
webpack会自动寻找webpack.config.js文件,你需要建立一个,然后填充以下内容:
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
}
}
其中
entry为入口文件,也就是未打包的、你项目的入口js文件;
而后output为打包后的文件,也就是生成的js文件存放的位置及名称。
其中__dirname为当前位置的意思
打包
命令行直接通过webpack打包
# {extry file}出填写入口文件的路径
# {destination for bundled file}处填写打包文件的存放路径
# 填写路径的时候不用添加{}
webpack {entry file} {destination for bundled file}
以上方法不通过webpack.config.js也可以进行,但不推荐,如果你进行了配置,只需要在目录
webpack(非全局安装需使用node_modules/.bin/webpack)
首先进行package.json的配置
{
"name": "webpack-sample-project",
"version": "1.0.0",
"description": "Sample webpack project",
"scripts": {
"start": "webpack",
"pack": "webpack"
},
"author": "zhang",
"license": "ISC",
"devDependencies": {
"webpack": "3.10.0"
}
}
然后你就可以
npm start
来进行打包了,这里其实只是给了npm一个快捷命令,npm start一定程度上与webpack命令相同,只不过可能在寻找执行程序上更为智能(毕竟是npm自己装的)
还有一个需要注意的点是在以上配置中,只有start是可以直接 npm start的,其他命令(比如pack)都需要使用
npm run pack
来进行(难道是npm把start写死了?这么酷炫?)
那么,基本的webpack基本的使用就结束了,但是如果只是这样使用价值不高,下面进行
真正的入门
开发相关
搭建本地服务器webpack-dev-server
先安装
npm install --save-dev webpack-dev-server
再配置(更改webpack.config.js文件)
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
},
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//无论地址栏输入什么,都指向index.html,for单页面应用
inline: true,//保存后自动刷新
port: "8080"//localhost:xxx的端口号,一般不需要指定,此项不写默认8080
}
}
最后运行
webpack-dev-server --open
猜猜--open是什么意思,嗯,就是会打开一个新页面/标签页。
也可以配置在package.json,这样更为简便
"scripts": {
"start": "webpack",
"dev": "webpack-dev-server --open"
},
记得要
npm run dev
并且,要注意的是,自动打包不会将新的webpack配置涵盖在内,如果你修改了配置,记得手动重新打包
使用source maps
这是一项配置,不需要安装,能在打包后,依然能找出是哪个文件出了bug
在webpack.config.js中进行配置如下
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
}
}
其中devtool的值代表以下配置
devtool选项 | 配置结果 |
---|---|
source-map | 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map ,但是它会减慢打包速度; |
cheap-module-source-map | 在一个单独的文件中生成一个不带列映射的map ,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便; |
eval-source-map | 使用eval 打包源文件模块,在同一个文件中生成干净的完整的source map 。这个选项可以在不影响构建速度的前提下生成完整的sourcemap ,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项; |
cheap-module-eval-source-map | 这是在打包文件时最快的生成source map 的方法,生成的Source Map 会和打包后的JavaScript 文件同行显示,没有列映射,和eval-source-map 选项具有相似的缺点; |
实用的功能
loaders
顾名思义,loaders根据相应文件的文件进行特别的加载和转译,如ES6转兼容性js,less转CSS等等,可以说是非常实用的。
一些js的转译(编译)需要Babel,既要配置loaders,又要安装相应Babel
先安装一些Babel
// npm一次性安装多个依赖模块,模块之间用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react
babel-core:babel的核心模块
babel-loader:loader与babel的连接模块
babel-preset-env:ES6->ES5的转译模块
babel-preset-react:react插件转译模块,为什么不直接叫babel-preset-jsx呢,who knows
然后进行loader配置,在webpack.config.js文件中
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
},
devtool: 'eval-source-map',
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true//实时刷新
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
}
]
}
};
这个配置表示要怎样的preset
这样就你就可以自由的写ES6和JSX了,可以自己试验一下
css modules
如今流行的是模块式开发,但是css这位大佬明显没能跟上浪潮
一个主页面的.title显然要和弹出框的.title有不同的样式。但是如果我们直接引入几个.css文件显然就会冲突,而区分命名还会徒增工作量,也有悖于模块化思想。
于是就有了这个css modules
其实解决方式很简单,它会给你的类名等等加上一串与所在文件相关的哈希字符串,这样,就不会冲突了,每个模块的.title都将拥有自己的名称
直接配置在webpack.config.js
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
},
devtool: 'eval-source-map',
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true//实时刷新
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
}
]
}
]
}
};
不过要想真正生效,你需要这样写(ES6):
import styles from './Greeter.css';
className={styles['title']}
如果你想直接引入,比如你想用css来微调UI框架的一些样式,可以直接:
import './Greeter.css';
不过这种样式会在文件被引入后持续生效,影响子组件,请谨慎使用
CSS预处理器(.less等)
.less这种文件对复杂的样式编写起来会很有逻辑,也会减少一定的代码量。详情请自行搜索,总之又是对CSS的一波嘲讽,不多说,安装:npm install --save-dev postcss-loader autoprefixer
postcss-loader:处理CSS与PostCSS的模块
autoprefixer:
自动添加前缀的插件
配置webpack.config.js
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
},
devtool: 'eval-source-map',
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true//实时刷新
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
}, {
loader: "postcss-loader"
}
]
}
]
}
};
新建个postcss的配置文件postcss.config.css
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
放心,会自动引用的
Plugins(插件)
如果说loaders是来幼儿园找孩子的妈妈,那plugins就是幼儿园的管理老师,它雨露均沾,一般不会区分什么文件。(这样说不会混了吧)
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
},
devtool: 'eval-source-map',
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true//实时刷新
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
}, {
loader: "postcss-loader"
}
]
}
]
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究')
],
};
这个插件能让打包后的js文件有一个'版权所有'的注释,或者你也可以写上其他吓吓小朋友的词句。
接下来重量级登场了,一个应用很频繁的插件
热加载
虽然webpack内置了热加载模块,不过webpack的热加载模块通常需要相关Babel配合
比如React就需要react-tarnsform-hrm,我们先安装它
npm install --save-dev babel-plugin-react-transform react-transform-hmr
然后进行配置,考虑到Babel的配置越来越多了,我们新建一个会被自动引用的.babelrc来专门配置Babel
// .babelrc
{
"presets": ["react", "env"],
"env": {
"development": {
"plugins": [["react-transform", {
"transforms": [{
"transform": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}]]
}
}
}
然后修改主配置文件webpack.config.js
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
},
devtool: 'eval-source-map',
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true//实时刷新
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
}, {
loader: "postcss-loader"
}
]
}
]
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数,这里为一个基础html文件
}),
new webpack.HotModuleReplacementPlugin()//热加载插件
]
};
这样就会热加载了
压缩
压缩需要多项配置来进行,最先要做的就是把刚才记在小本本上的devtool的值改为'null'
然后再配置中的plugins项中加入
new webpack.optimize.UglifyJsPlugin(),
暂时入门内容就到这里,楼主会随着学习的深入进行更新,欢迎讨论