若有错误请大家指出!谢谢
一、webpack的简单介绍
🎈注意:webpack 的核心定义是 "模块打包工具“ ,而并非转译工具
它支持:
- ES Module模块引入方式
import A from './a.js' - Common Js 模块引入规范
var A= require("./a.js) - CMD 规范
- ADM 规范
// header.js
function Header(){
var dom = document.getElementById('root')
var header = document.createElement("div")
headr.innerText = '我是 header'
dom.append(header)
}
// commonJS 规范
//被导出的js文件需要添加
module.exports = Header;
// ES Module规范
//被导出的js文件需要添加
export default Header;
index.js 我们在这里引入 header.js文件
// COmmonJS 规范
var Header = require("./header.js");
// ES Module规范
import Header from "./header.js"
小扩展: webpack在最开始的时候,只能打包js文件,随着webpack的不断升级,现在(4.X)已经能打包css,png, jpg等任何你想打包的文件了
// 安装及版本查看
npm install webpack@4.25.0 -D
npx webpack -v (npx会到nodemodules中查询版本号)
执行npx webpack index.js 即可打包,在根目录下会生成一个dist文件夹,其内部会有一个js 文件,具体的的文件名,可以在webpack.config.js中自行配置
二、webpack的配置文件
🎈webpack的默认配置文件是webpack.config.js
// 为了让output的path,能指定绝对路径 ,需要先引入node 自带的 path模块
const path = require('path')
// common JS的规范写法(module.exports)
module.exports = {
// entry 代表 需要打包的入口文件, 如果不指定,则使用此处定义的文件。
// 当没有此文件 (webpack.config.js) 的时候 默认使用 npx webpack 会报错 , 但如果指定某文件进行打包 : npx webpack XXX.js 则可执行
// 因为 没有配置项 ,则没有默认打包文件 ,即报错; 因此需要在此处配置默认文件
entry: './index.js',
// output 代表打包后的文件输出路径
output: {
// filename 代表文件名的key ,值是具体的value
filename: 'bundle.js',
// path 后面需要跟绝对路径, 为了填写路径, 需要引入node的path模块(最开始已经引入)
// 第一个参数 :__dirname 代表的意思是 webpack所在的当前目录的路径
// 第二个参数 :'bundle' 这样一来拼接的路径就是 "打包生成的文件" 的绝对路径了
path: path.resolve(__dirname, 'bundle')
// 因此现在的流程就是:
// 0. npx webpack 因为有了默认文件的存在,不再需要指定文件
// 1. 找到webpack.config.js,寻找entry入口, 发现需要打包的文件是index.js
// 2. 需要输出的路径是path 拼接得到的绝对路径
// 3. 输出的文件 文件名是 filename上 指定的 'bundle.js'
// 4. 在根目录下,多出一个bundle文件夹,内部生成一个打包好的bundle.js 此文件的本质 实际上是index.js
}
}
注意: 默认情况下,必须指定webpck.config.js文件进行打包,否则会报错。
如果有需要改动文件名,则应使用 "npx webpack --config XXX.js" 指令进行指定某个打包文件的修改:npx webpack --config mywebpack2333.js
这样即可将 mywebpack2333.js 作为配置文件进行打包了
🎈 在package.json中对指令进行统一管理
在package.json文件中,我们可以找到scripts 字段,打包指令过于臃肿,我们可以在此处进行指令简化"scripts":{"bundle": "webpack"}
下一次需要打包的时候,直接输入 npm run bundle 即可
在我们执行 "npm run bundle" 之后:
可以看到已经成功打包了。
三、打包静态图片资源
👀 我们来关注下 webpack.config.js 中的 module 部分
mudule是对于不同的模块的处理规则。
module的rules属性代表不同的处理规则。rules是一个对象。对象的属性有test、use、exclude、include。
rules:[
{
test : /.\png$/, // 当匹配的对象是以png结尾的文件
// test : /\.(png|jpg|gif)$/ 表示 以png\gif\jpg结尾的文件
use:{
// 就调用 loader 中的 file-loader 进行打包,文件同样生成在 dist 文件夹下,
loader: 'file-loader',
options:{
name: '[name].[ext]',
// 打包生成的文件名称配置,旨在让生成的文件,后缀与文件名都与源文件保持一致。也可以写成:name: '[name]_[hash].[ext]' hash 是文件被打包生成的hash值
// ext 是 placeholder占位符 中的一种,在官网有介绍 ([hash]也是)
//
outputPath:'images/' // 将此类型打包生成的文件放置于 "disat/images" 文件夹下,若没有此配置项,则依然放置于dist文件夹下
}
}
}]
rules数组中的对象的test用于匹配某种类型的文件,若通过test的匹配,则会执行当下的use:{...}
loader : 指定使用何种类型的loader;
options : name 可以指定文件名; outputPath 指定文件具体的存放路径 文件存储的具体地址。
如果两个都不指定,则会以打包生成的hash值,作为文件名,文件类型不变。位置存放于dist文件夹下。
小扩展: 上面的demo中, 也可以使用 url-loader 进行 转换。 另外在 outputPath 的同级 ,url-loader 还有一个 limit 字段,此字段设置的是一个大小范围。单位是字节。
当小于这个大小的时候,url-loader会将图片 以base64格式,存在bundle.js 中,以减少请求次数。但是当图片大小超过此范围的时候,webpack就会将图片单独打包。
因此范围一定需要合理,否则大图在存在js中,会非常影响渲染速度。白屏时间严重加长。
四、打包样式[上]
注意:新写法
{
test:/\.css$/,
use:['style-loader','css-loader']
}
2020.1.27
{
test : /.scss$/,
use:{
loader: [
'style-loader',
'css-loader',
'sass-loader',
'postcss-loader' // 用于为css增加厂商前缀 -webkit--XXX \ -firefox--XXX
],
// loader 的顺序是 自下而上、 自右而左。
// 因此先使用sass-loader 将scss 文件转化成普通的 css代码,再 由css-loader 理清 css的引用关系 ,最后由style-loader 解读样式代码
}
}
当webpack打包样式文件的时候,会遇到css、styls、scss等样式文件。此时的webpack需要借助loader进行打包。因此,我们需要先安装可能出现的loader。
npm i style-loader css-loader -D
css-loader用于理清css文件之间的引入关系。style-loader用于分析css代码
npm i sass-loader node-sass -D
用于将scss文件中的代码 翻译成 普通css代码
npm i postcss-loader -D
用于 为开发者增加厂商前缀 -webkit--XXX \ -firefox--XXX 等 在 postcss-loader 安装完毕之后。需要先在项目根目录下创建一个 postcss.config.js 的
postcss配置文件。此文件书写如下:
module.exports = {
plugins: [
require("autoprefixer")
]
}
当 webpack.config.js 中loader按顺序执行的时候,遇到了postcss-loader,就会去寻找 postcss.config.js 文件。将按照此文件的配置信息,在plugins中发现需要使用autoprefixer。开始下一步的操作
另外 loader 的顺序是 自下而上、 自右而左执行。顺序错误,可能导致无法成功打包或打包失败
五、plugins -- 让打包更便捷
⭐ htmp-webpack-plugin
👍htmlwebpackplugin 会在打包时 帮助你添加一个 index.html, 否则每次需要手动添加就很低能。且添加的同时也会帮助开发者将bundle.js引用到html文件中
先安装plugin cnpm install html-webpack-plugin -D
使用前需要在 webpack.config.js 中 引入 var HtmlWebpackPlugin = require('html-webpack-plugin');
接着在plugins 字段中,将对象实例化一下 plugins:[new HtmlWebpackPlugin()]
虽然 htmlwebpackplugin帮助我们引入了js文件,但美中不足的是,他并不会帮助我们添加id为root的div。 而我们的项目是通过document.getelementById进行挂载的。我们可以这样对webpack.config.js的plugins字段 进行配置:
意思是,使用在src目录下的index.html文件作为模板,进行html文件的再造(添加bundle.js)
⭐ clean-webpack-plugin
如果我们现在将输出改成dist.js,再进行打包。在dist文件夹下的确能够生成一个dist.js,并且 html-webpack-plugin 也能为我们将html文件中注入dist.js。但是上一次打包生成的bundle.js依然存在。这就使得体验很不好。我们可以借助 clean-webpack-plugin 来完成此操作(删除往次生成的被重命名的文件)
执行 cnpm i -D clean-webpack-plugin
//旧版本写法:报错!
//引入
`const CleanWebpackPlugin = require('clean-webpack-plugin')`
//实例化对象
`plugins:[new CleanWebpackPlugin(['dist'])] `
---
// 报错提示是: CleanWebpackPlugin is not a constructor
---
//新版本写法:
const {CleanWebpackPlugin} = require('clean-webpack-plugin')`
plugins:[new CleanWebpackPlugin()] `
//这里不用指定plugins 的CleanWebpackPlugin 路径原因是: 会自动删除新生成文件的旧版文件(将生成文件的上一个版本删除)
另外使用plugin或其他引入需要注意:横杠连接处一定要用大写,是一种规范。贴一张图(来自博客园博主 - 浮华夕颜):
注意:plugin是由打包顺序的,比如此处的html-webpack-plugin 在打包后执行,而clean-webpack-plugin是在打包前执行
笼统的概括plugin的话,plugin实际上就是在代码执行到某个时刻时,它会自动帮你去做一些事情。这有点类似生命周期的钩子
六、Entry与Output基础配置
🙃在我们实际工作中,很多时候是需要打包多个js文件的,因此在entry中,我们需要这样写:
entry:{ main : './src/index1.js',sub : './src/index2.js' }
而我们的 output 中:
OutPut:{output: filename: 'bundle.js', path: path.resolve(__dirname, 'dist')}
难道让两个被打包的文件重名然后报错? 😡当然不能,上文有提及到[name].[ext]的概念,这里就可以这样使用了
OutPut:{output: filename: '[name].[ext]', path: path.resolve(__dirname, 'dist')}
// 这里再次复述一遍: [name] 是源文件的文件名、[hash]是文件被打包时候生成的哈希值、[ext]是源文件的后缀。
这样一来,打包结束后,会生成两个文件,且在html-webpack-plugin的帮助下,两个js都会被引入
上图:
七、SourceMap
sourcemap相当于配置一个映射,比如初始代码是a.js ,a.js被打包过后会生成b.js并引用到html中去,
此时如果a.js中有错,那么打包生成的b.js也是有错的。但是当我们点击开发控制台上的按钮时,控制台只会跳转到b.js的某一行。但是我们需要在a.js中找到错误所在,从而解决根本。
这时候sourceMap,就能完美帮我们解决这个问题。直接跳转到 a.js 出错的地址。且打包结束后会在dist目录下生成一个.map文件,此文件实际就是映射关系
接下来我们来看看如何配置:
八 、 使用 webpackDevServe 提高运行效率
目前来说,我们每修改一次src目录下的代码,就需要重新,npm run bundle 一次。在真实情况下,这样的操作,无疑效率十分低下。
因此我们将webpack 的 package.json文件中写成
"scripts": {"watch": "webpack --watch"}
因此下一次执行的时候,输入 npm run watch ,一旦源文件发生变化,则webpack会自动重新打包文件. 从而起到刷新的作用.
但当我们第一次执行npm run watch 希望自动帮我们执行打包、更新、打开浏览器、模拟服务器特性等操作时。一句简单的npm run watch 就不行了,此时我们需要webpack-devserver 来帮助我们完成更强大的功能。
WebpackDevServer
WebpackDevServer在webpack内部,并没有内置,我们需要先安装它。npm i WebpackDevServer -D
接下来我们只需要在 webpack.config.js 上:
-
添加配置
devServer:{ contentBase: './dist' },//我们需要让webpack帮助我们启一个服务器,此服务器的位置建立在dist目录下, -
在package.json中scripts配置项中添加:
"start": "webpack -dev-server"
现在我们直接执行: npm run start
此时我们可以直接通过浏览器访问8080端口打开网站。
webpackdevserver和watch的好处比较是:webpackdevserver不仅能够监听到文件发生了改变,需要重新打包;更帮助我们同时刷新浏览器,刷新控制台输出的内容!
我们再配置一条:
注意:webpack-dev-server 执行后并没有生成dist目录,这并不是没有打包成功的表现。而是webpack-dev-server 将打包的内容存在了内存中,因此会进一步提升打包的速度
九、Hot Module Replacement(hmr) -- 热模块替换
我们先将package.json中scripts的配置项只留下"start": "webpack-dev-server"
现在我们启动 npm run start
style.css代码:
我们可以看到,在浏览器上:
hmr 登场
我们先到webpack.config.js中,为dev-server项添加 hot: true字段,意为启用热模块更新;安全起见,再添加一条 hotOnly:true 意为即便热模块更新没有启用,那么也阻止浏览器自动刷新
接下来引入一个webpack的plugin 实例化webpack对象:
上述两项搞定之后最好重启下服务,npm run start,hrm就开启并可以使用了.
十、使用Bable处理es6语法
我们首先将index.js中的代码替换:
接下来我们使用npx webpack 进行打包(不使用webpack-dev-server打包的原因是:此方法会将打包的结果储存在内存里,我们就无法观察有无babel情况下打包的异同)
注意:chrome是可能执行成功的,他已经对es6语法做了技术支持,能够辨认。但如果是一些国产浏览器、ie低版本在没有babel的情况下是一定报错的。
安装babel:
cnpm install --save-dev babel-loader @babel/core
webpack.config.js 添加配置项:
babel-loader还不够, babel-loader只是webpack与babel之间的桥梁。他并不会帮助我们将es6的语句转换成es5的。我们还需要:npm install @babel/preset-env --save-dev
@babel/preset-env 这个模块里,包含了所有 es6 转换成 es5 的规则。
安装完毕后,完成如下配置就能将所有es6转换成es5了。
{
test: /\.js$/, // 当匹配到 不属于node_modules下的js文件, 以babel-loader进行打包
exclude: /node_modules/, // 因为node_modules 下的js文件都是第三方模块,已经做好了处理
loader: "babel-loader",
options:{
"presets": ["@babel/preset-env"]
}
}
到这里我们再次执行:npx webpack
注意:
由于此处学习的版本和老师讲解存在差异。上述操作后出现大面积报错。原因是babel的配置出现了问题。 具体详见此处: webpack配置babel时候报错ERROR in ./src/main.js Module build failed (from ./node_modules/babel-loade.
步骤:
- 先卸载babel的安装
cnpm uni babel-core babel-loader babel-plugin-transform-runtime -D - 执行新的安装
cnpm i babel-loader @babel/core @babel/runtime @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime -D - 安装依赖
cnpm install --save-dev babel-plugin-transform-object-rest-spread - 配置.babelrc文件
{
"presets": [
"@babel/preset-env"
],
"plugins": [
"@babel/plugin-transform-runtime",
"@babel/plugin-proposal-class-properties"
]
}
接下来再执行 npx webpack 会依次出现 cannot found html-webpack-plugin 和 cannot found clean-webpack-plugin .
猜测可能是新版本的出现,之前的配置方法失效导致,初学也没有更好的办法,因此就执行了: cnpm i html-webpack-plugin clean-webpack-plugin -D
最后 npx webpack 打包终于不报错了,来看一下打包生成的文件:
可以发现,babel已经帮我们将es6的语法转换成es5了。箭头函数也转换成了普通的function(){}. 成功。
美中不足的是,现在我们虽然将语法转换了,但是在一些低版本的浏览器中,promise对象及数组的map函数都是不存在的,他们并不能识别这些内容,所以我们不仅需要 **babel-preset **做语法转换,还需要使用 babelpolyfill 把这些变量补充道低版本的浏览器中。
cnpm i @babel/polyfill -D 安装polyfill
使用polyfill很简单,我们只需要在需要打包的js文件首行 import 即可
可以看到使用polyfill补充语句之后,大暴出来的目标文件变得非常大了。在先前只有10kb不到的大小,现在是700kb。
所以我们需要优化,我们只希望babelpolyfill补充我们在index.js中使用到的es6新语句。 修改webpack.config.js 的配置项:
如果你想给某一项配置参数,那么你需要将其放入一个中括号中,第一个使用的present的名字,第二个参数 放置的是配置参数
文件大小就小了很多了