学习webpack的时候,看过它的官网,发现非常难懂,各种内容编排的很乱,并且挺生涩难懂的,和React Native等项目的官网比起来,差太多了,所以如果刚开始接触webpack的话,不建议直接去其官网看。
webpack是一个module bundler(模块打包工具),所谓的模块就是在平时的前端开发中,用到一些静态资源,如JavaScript、CSS、图片等文件,webpack就将这些静态资源文件称之为模块。
webpack支持AMD和CommonJS,以及其他的一些模块系统,并且兼容多种JS书写规范,可以处理模块间的以来关系,所以具有更强大的JS模块化的功能, 它能对静态资源进行统一的管理以及打包发布 ,在官网中用这张图片介绍:
它在很多地方都能替代Grunt和Gulp,因为它能够编译打包CSS,做CSS预处理,对JS的方言进行编译,打包图片,代码压缩等等。所以在我接触了gulp之后,就不太想用gulp了。
在自己的学习使用中、和技术交流群里以及和朋友的讨论、网上其他大神的博客中得到一些为什么要使用webpack的原因,webpack的优点,webpack更适用于哪里。
对 CommonJS 、AMD 、ES6的语法做了兼容;
对js、css、图片等资源文件都支持打包;
串联式 模块加载器 以及 插件机制 ,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持;
有独立的配置文件webpack.config.js;
可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间;
支持 SourceUrls 和 SourceMaps,易于调试;
具有强大的Plugin接口,大多是内部插件,使用起来比较灵活;
webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快;
webpack最常用与spa应用,主要是vue和React,其实它就非常像Browserify,但是将应用打包为多个文件。如果单页面应用有多个页面,那么用户只从下载对应页面的代码. 当他么访问到另一个页面, 他们不需要重新下载通用的代码。
webpack也能用于服务端,但是 构建后端代码一般都不会用webpack ,坑太多了,所以正常情况下只用于前端,这是一个做了很多年前端的朋友告诉我的经验,所以别人已经走过的坑,还是不要去踩了~~~~ 并且我个人觉得后端其实也没必要这么做,因为后端更多的是处理逻辑,以及为前端提供数据.....
npm install webpack -g
在项目的根目录创建三个或多个webpack配置文件,我们创建两个:
首先先配置开发环境,写上最基本的内容:
var path = require('path');
module.exports = {
entry:path.resolve(__dirname,'src/js/app.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
}
最基本的配置文件中一般含有文件入口和输出文件的配置信息,后期还可以加上loader和各种插件配置使用。
在根目录运行 webpack
命令, 默认会去查找名称为webpack.config.js的文件执行 ,如果没有就会报配置信息没有配置的错误。
webpack
由于我们是将开发环境和发布环境分开的,并没有直接添加webpack.config.js,所以这种方式在我们这里显然是不行的;
通过运行下面的命令进行配置文件的选择
webpack --config webpack.dev.config.js
命令行输出如下:
zhongfabindeMacBook-Pro:webpack_test zhongfabin$ webpack --config webpack.dev.config.js
Hash: d8151fcc7a02add3ec93
Version: webpack 2.2.1
Time: 49ms
Asset Size Chunks Chunk Names
bundle.js 2.57 kB 0 [emitted] main
[0] ./src/js/app.js 63 bytes {
0} [built]
在dist中生成了bundle.js:
这时候如果想看生成的js是否能用,我们需要将src中的拷贝一份到dist中,并且引入bundle.js(后面可以自动生成index,现在后没学到):
运行效果如下:
注意:在实际的开发中,我们基本上不会用以上这种直接提供的命令,因为用这种方式,我们需要手动的敲打很多字母,在实际的开发中通用的方法都是使用配置文件的方式运行(接下来讲解)。
npm 是一个非常好用的用来编译的指令,通过 npm 你可以不用去担心项目中使用了什么技术,你只要调用这个指令就可以了,只要你在 package.json 中设置 scripts 的值就可以了。
真正开发的时候你的webpack的命令会敲很长,因为有很多命令需要设置,即便你用了配置文件的方式,但一些--colors --hot这些命令是没法在配置文件中配置的。比如:
webpack-dev-server --devtool eval --progress --colors --hot --content-base build
所以我们不可能每次都敲这么长,因而我们就把这一大串配置到npm中
安装webpack, 注意:这时候不是全局安装
npm install --save-dev webpack
配置npm的package.json文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev":"webpack --config webpack.dev.config.js",
"pub":"webpack --config webpack.pub.config.js"
},
在根目录下执行命令
npm run dev
这样之后就不要敲那么复杂的命令了。
我们没修改一次就要需要输入 npm run develop 是一件非常无聊的事情,幸运的是,我们可以把让他自己运行,那就是使用webpack-dev-server。
除了提供模块打包功能,Webpack还提供了一个基于Node.js Express框架的开发服务器,它是一个静态资源Web服务器,对于简单静态页面或者仅依赖于独立服务的前端页面,都可以直接使用这个开发服务器进行开发。在开发过程中,开发服务器会监听每一个文件的变化,进行实时打包,并且可以推送通知前端页面代码发生了变化,从而可以实现页面的自动刷新。
使用步骤:
安装webpack-dev-server
npm install --save-dev webpack-dev-server
调整npm的package.json中scripts 部分开发命令的配置
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"pub": "webpack --config webpack.pub.config.js",
"dev": "webpack-dev-server --config webpack.dev.config.js --devtool eval --progress --colors --hot --content-base src"
}
}
在dev的配置中做了以上改变之后,webpack-dev-server 会在 localhost:8080 建立一个 Web 服务器。
几个参数的解释:
手动访问 http://localhost:8080
简单来说,当你运行 npm run develop 的时候,webpack会帮你会启动一个 Web 服务器,然后监听文件修改,然后自动重新合并你的代码。真的非常简洁。
注意点
几个报错
webpack版本的问题
如果webpack使用的1.x的版本,那么webpack-dev-server也要使用1.x的版本,否则会报如下错误:Connot find module 'webpack/bin/config-yargs'。
端口占用问题
如果已经有一个工程中使用了webpack-dev-server,并且在运行中,没有关掉的话,那么8080端口就被占用了,此时如果在另一个工程中使用webpack-dev-server就会报错:Error: listen EADDRINUSE 127.0.0.1:8080。
做完以上的所有步骤之后,可以启动一个服务并监听变化了,但是浏览器你还需要手动刷新一下,我们其实是可以通过下面的这个方法让它自动刷新的。
修改配置文件中的entry部分,当前entry的的值为:
entry:path.resolve(__dirname,'src/js/app.js')
将entry的值改为一个数组,除了上面的内容加进去之外,再加其他两句:
var path = require('path');
module.exports = {
entry: [
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8080',
path.resolve(__dirname,'src/js/app.js')
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
}
特别注意:此时用webpack-dev-server生成bundle.js文件是在内存中的,所以在src中的index引入的js文件应该生成的bundle.js,如果还是引入的app.js的话,将会看不到刷新的效果。
加载器就是webpack准备的一些预处理工具,比如编译jsx和es6的加载器,处理sass等....
使用加载器的步骤也很简单,首先是安装依赖,然后在配置文件的module中加一个字段module字段,在module写上loaders,在loaders中写上相应的配置。
npm install --save-dev babel-loader babel-core babel-preset-es2015 babel-preset-react
修改开发配置文件
module: {
loaders: [
{
test: /\.jsx?$/, // 用正则来匹配文件路径,这段意思是匹配 js 或者 jsx
loader: 'babel',// 加载模块 "babel" 是 "babel-loader" 的缩写
query: {
presets: ['es2015', 'react']
}
}
]
}
使用
直接从新运行npm run dev即可
Webpack允许像加载任何代码一样加载 CSS。你可以选择你所需要的方式,但是你可以为每个组件把所有你的 CSS 加载到入口主文件中来做任何事情。
加载 CSS 需要 css-loader 和 style-loader,他们做两件不同的事情,css-loader会遍历 CSS 文件,然后找到 url() 表达式然后处理他们,style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中。
首先下载依赖
npm install --save-dev css-loader style-loader
修改配置文件,在loaders中加上
{
test: /\.css$/,
loader: 'style!css' // 如果有多个加载器,中间用感叹号隔开,多个加载器从右往左执行
}
“!”用来定义loader的串联关系,"-loader"是可以省略不写的,多个loader之间用“!”连接起来
CSS的加载策略
import './project-styles.css';
使用内联样式取代 CSS 文件
在 “React Native” 中你不再需要使用任何 CSS 文件,你只需要使用 style 属性,可以把你的 CSS 定义成一个对象,那样就可以根据你的项目重新来考虑你的 CSS 策略。
下载依赖
npm install --save-dev sass-loader
修改配置文件
{
test: /\.scss$/,
loader: 'style!css!sass'
}
使用和导入和CSS的使用方式一致
直到 HTTP/2 你才能在应用加载的时候避免设置太多 HTTP 请求。根据浏览器不同你必须设置你的并行请求数,如果你在你的 CSS 中加载了太多图片的话,可以自动把这些图片转成 BASE64 字符串然后内联到 CSS 里来降低必要的请求数,这个方法取决与你的图片大小。你需要为你的应用平衡下载的大小和下载的数量,不过 Webpack 可以让这个平衡十分轻松适应。
下载依赖
npm install --save-dev url-loader file-loader
修改配置文件
{
test: /\.(png|jpeg|gif|jpg)$/,
loader: 'url?limit=25000'
}
加载器会把需要转换的路径变成 BASE64 字符串,在其他的 Webpack 书中提到的这方面会把你 CSS 中的 “url()” 像其他 require 或者 import 来处理。意味着如果我们可以通过它来处理我们的图片文件。
url-loader 传入的 limit 参数是告诉它图片如果不大于 25KB 的话要自动在它从属的 css 文件中转成 BASE64 字符串。
大图片处理
在代码中的情况是以下之一:
div.img{
background: url(../image/xxx.jpg)
}
//或者
var img = document.createElement("img");
img.src = require("../image/xxx.jpg");
document.body.appendChild(img);
你可以这样配置:
{
test: /\.(png|jpeg|gif|jpg)$/,
loader: 'file-loader?name=images/[name].[ext]'
}
针对上面的两种使用方式,loader可以自动识别并处理。根据loader中的设置,webpack会将小于指点大小的文件转化成 base64 格式的 dataUrl,其他图片会做适当的压缩并存放在指定目录中。
前面一直都在捣鼓config webpack.dev.config.js,也就是一致都在配置开发环境,现在重要配置发布环境了,也就是配置config webpack.pub.config.js,其实发布的配置和开发的配置差不了太多。
修改npm的package.json文件
其实相关的配置早就加上去了,就是以下的这句:
"pub": " webpack --config webpack.pub.config.js -p",
指向生产的配置文件,并且加上了webpack的cli的-p,他会自动做一些优化
修改webpack.pub.config.js文件
其实将webpack.dev.config.js中的内容拷贝过来即可,然后修改entry中的一些东西。
var path = require('path');
module.exports = {
entry: path.resolve(__dirname, 'src/js/app.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel',
query: {
presets: ['es2015', 'react']
}
},
{
test: /\.css$/,
loader: 'style!css'
},
{
test: /\.scss$/,
loader: 'style!css!sass'
},
{
test: /\.(png|jpeg|gif|jpg)$/,
loader: 'file-loader?name=images/[name].[ext]'
},
]
},
}
可以看到,其实生产环境的配置和开发的配置没有太大的不同,主要是把不需要的东西给去掉了。
直接运行:npm run pub
当你的应用依赖其他库尤其是像 React JS 这种大型库的时候,你需要考虑把这些依赖分离出去,这样就能够让用户在你更新应用之后不需要再次下载第三方文件。当满足下面几个情况的时候你就需要这么做了:
当你的第三方的体积达到整个应用的 20% 或者更高的时候
更新应用的时候只会更新很小的一部分
你没有那么关注初始加载时间,不过关注优化那些回访用户在你更新应用之后的体验
通过手机访问的用户比较多
在头部引入webpack
var webpack=require("webpack");
修改entry
增加plugins
结果:在dist文件夹中生成两个js文件,一个bundle.js,另一个是vendors.js
webpack提供了丰富的组件用来满足不同的需求,当然了我们也可以自行实现一个组件来满足自己的需求。
plugins: [
//your plugins list
]
这个插件是webpack自带的
// 用webpack压缩代码,可以忽略代码中的警告
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
在webpack中编写js文件时,可以通过require的方式引入其他的静态资源,可通过loader对文件自动解析并打包文件。通常会将js 文件打包合并,css文件会在页面的header中嵌入style的方式载入页面。但开发过程中我们并不想将样式打在脚本中,最好可以独立生成css文件,以外链的形式加载。这时extract-text-webpack-plugin插件可以帮我们达到想要的效果。需要使用npm的方式加载插件,然后 参见下面的配置,就可以将js中的css文件提取,并以指定的文件名来进行加载。
安装依赖
npm install --save-dev extract-text-webpack-plugin
使用
// 可以新建多个抽离样式的文件,这样就可以有多个css文件了。
new ExtractTextPlugin("app.css"),
plugins: [
// 可以新建多个抽离样式的文件,这样就可以有多个css文件了。
new ExtractTextPlugin("app.css"),
]
修改loaders中的配置:
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style-loader' , 'css-loader')
},
注意:我发现这个有一个问题,就是他只能把css抽出来,但是sass的样式不能分离出来。
安装依赖
npm install --save-dev html-webpack-plugin
使用
// 自动生成index.html页面插件
var HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
template: './src/template.html',
htmlWebpackPlugin: {
"files": {
"css": ["app.css"],
"js": ["vendors.js","bundle.js"]
}
},
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
}
}),
]
创建模板文件:template.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="keyword" content="webpack,gulp,git">
<title>modeltitle>
head>
<body>
<div id="app">div>
body>
html>
在开发环境中使用,run之后自动打开浏览器
安装依赖
npm install --save-dev open-browser-webpack-plugin
使用
// 自动打开浏览器插件
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
plugins: [
new OpenBrowserPlugin({url: 'http://localhost:8080/'})
]
在开发环境中使用,run之后自动打开浏览器
安装依赖
npm install --save-dev clean-webpack-plugin
使用
// 删除文件夹
var CleanPlugin = require('clean-webpack-plugin');
plugins: [
// 构建之前先删除dist目录下面的文件夹
new CleanPlugin(['dist']),
]
plugins: [
new webpack.DefinePlugin({
//去掉react中的警告,react会自己判断
'process.env': {
NODE_ENV: '"production"'
}
}),
]