webpack笔记
为什么需要webpack
网页中会引入一大波静态文件,这样会导致:
- 网页加载慢,因为要发起很多二次请求。
- 要处理错综复杂的依赖关系
如何解决问题?
- 合并,压缩,精灵图,图片base64编码
- webpack解决依赖关系
打个比方
有这么一个index.html,并引入一个main.js
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./main.js"></script> </head> <body> <ul> <li>这是第1个li</li> <li>这是第2个li</li> <li>这是第3个li</li> <li>这是第4个li</li> <li>这是第5个li</li> <li>这是第6个li</li> <li>这是第7个li</li> <li>这是第8个li</li> <li>这是第9个li</li> <li>这是第10个li</li> </ul> </body> </html>
import $ from 'jquery' $(function(){ $('li:odd').css('backgroundColor', 'yellow') $('li:even').css('backgroundColor', 'red') })
打开浏览器,会报语法错误,因为浏览器不认识import这种语法,所以需要将main.js进行一个转化
基本配置
根目录添加一个webpack.config.js,设置入口和输出
entry: './src/main.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }
在package.json中添加
"scripts": { "build": "webpack" }
然后执行npm run build
,没毛病的话dist下就会出现一个bundle.js,然后在index.html中引入
<head> <!-- <script src="./main.js"></script> --> <script src="../dist/bundle.js"></script> </head>
这时候浏览器打开index.html就能看到效果了。但是每次都要自己运行npm run build
显得过于麻烦,应该要一修改就自动打包好就更方便了
webpack-dev-server
安装webpack-dev-server,npm i webpack-dev-server -D
由于是项目中安装,而非全局安装,因此,cmd里是没有webpack-dev-server这个命令的。编辑器里要用可以在package.json里面的scripts里面加上一句:
"scripts": { "dev": "webpack-dev-server" }
这样就可以通过npm run dev
来运行webpack-dev-server命令。成功的话日志里会有一句:Project is running at http://localhost:8080/
,打开这个地址就会看到当前项目的文件。
这个时候修改main.js就会重新打包bundle.js,按理说这个时候刷新网页能看到更新,但是实际上是没有任何变化。
要知道的是,webpack-dev-server打包生成的bundle.js并没有放到实际的物理磁盘上,而是在内存中。可以认为这个工具把打包好的文件以一种虚拟的形式托管到了项目的根目录中,和dist,src同一层级有一个看不见的文件叫做bundle.js
并且可以在 http://localhost:8080/bundle.js 中访问到这个文件。
因此更改bundle的引入路径,即可看到更新
<script src="/bundle.js"></script>
-
给webpack-dev-server传递参数
"dev": "webpack-dev-server --open --port 3000 --contentBase src --hot"
- --open自动打开浏览器
- --port 3000 修改端口为3000
- --contentBase src 以src作为内容根路径
- --hot 之前每次修改都会重新生成bundle.js,设置hot后则只会生成hot-update文件,相当于做了一个补丁更新过来。减少不必要的更新
还有一种写法给webpack-dev-server传递参数,在webpack.config.js里走配置:
devServer: { open: true, port: 3000, contentBase: 'src', hot: true }
webpack3.*貌似光设置hot: true还不行还得引入插件才行,4.x测试貌似只要hot:true就可以了
html-webpack-plugin
bunlde.js是内存中的,而index.html是物理磁盘中的,而html-webpack-plugin能把index.html也放到内存中去
安装:npm i html-webpack-plugin -D
webpack.config.js中添加
const htmlWebpackPlugin = require('html-webpack-plugin') module.exports = { plugins: [ new htmlWebpackPlugin({ // 根据指定的模板页面生成内存中的页面 template: path.join(__dirname, './src/index.html'), filename: 'index.html' }) ], }
npm run dev
看到的index页面,查看源码会发现在底部插入了一行
<script type="text/javascript" src="bundle.js"></script></body>
因此head中引入bundle的代码删除也没问题,这个插件会自动把这个bundle插入,并且不用去处理路径问题。
loader
当需要引入css的时候,如果在head中引入css那就又回到了老问题,会产生二次请求。应该在js里如import。
<head> <link rel="stylesheet" href="./css/index.css"> </head>
那么在main.js中引入css
import './css/index.css'
这个时候打包编译会报错You may need an appropriate loader to handle this file type
因为webpack只能处理js类型的文件,其他非js无法处理,需要安装一些第三方loader
npm i style-loader css-loader -D
然后在webpack.config.js中跟entry同级下添加
module: { rules: [ {test:/\.css$/, use:['style-loader', 'css-loader']}, ] }
rules下是所有第三方模块的匹配规则,用正则匹配css结尾的文件,use后配置对应的loader。调用规则是从右往左处理,先css-loader。所有loader处理完毕后再交给webpack去打包合并,最终输出到bundle.js中。
而如果要写less或者sass,也同理,需要对应的loader
拿less来说,需要安装less,和less-loader,less-loader内部依赖less
npm i less -D
npm i less-loader -D
然后如法炮制,配置上去,另外style-loader, css-loader也是需要的
{test:/\.less$/, use:['style-loader', 'css-loader', 'less-loader']}
当css中用url 引入了图片后也需要加loader才行,比如
.aa { width: 100px; height: 100px; background-image: url(../images/a.jpg); }
那么,需要安装loader
npm i url-loader file-loader -D
{test:/\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader'}
file-loader是url-loader内部依赖,因此不需要写进去。这个时候就能正常看到图片了。审查元素后会发现,图片是被转成了base64。
可以配置图片多大的时候才转base64,通过?来给loader传递参数,格式和url地址传参一样。
设置一个比图片小的值,这样就不再是base64,而是类似8b79eae7720bcc24929337b220c4a2fb.jpg这样的了。这一串数字是哈希值,为了避免重名。
{test:/\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=18421'}
选的图片大小是18422字节,只有比limit小的才会转成base64
也可以自己设置图片名字:比如
{test:/\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=18421&name=[name].[ext]'}
这样,就保持图片名字不变,但是这样如果有两张图片路径不同,但是名字相同,被引入了的话,最后打包里的只会存在一张图片。
也可以用hash加name的方式,name=[hash]-[name].[ext] 这样就是hash-name格式的名字了,保证了不重名也保留了本来的名字。
{test:/\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=18421&name=[hash]-[name].[ext]'}
Babel
在webpack中默认只能处理一部分ES6的新语法,一些更高级的ES6,ES7语法无法处理。需要第三方loader来把高级语法转成低级语法之后,再又webpack打包处理。Babel可以干这个事。
比如,main.js中有如下代码
class Person { static info = { name:'tracy', age:99 } say = () => { console.log('hello') } } console.log(Person.info) let p = new Person() p.say()
打包会报错,webpack表示并不认识这些语法。根据官网:webpack4.x
npm install -D babel-loader @babel/core @babel/preset-env webpack
npm install -D @babel/plugin-transform-runtime
npm install @babel/runtime
然后配置rules
{ test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], plugins: ['@babel/plugin-transform-runtime'] } } }
我这样搞下来还是有报错,说不认识static
,要安装一个@babel/plugin-proposal-class-properties。我就试着安装了下,然后配置到plugins上去。
npm install -D @babel/plugin-proposal-class-properties
plugins: ['@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime']
貌似就可以了,很和谐