Hi ~ 许久没更新的小简书,自从换了新工作,日夜掉发式的疯狂加班,一个半月的时间学到了不少东西,趁热记录一波吧~
一个月前,接到了一个需求,并且长期需要维护多个无关的活动页或者场景页,自以为简单的我乐呵呵的接了需求,心中已经想好“用react来搭一个多页面应用就够符合需求啦~”,事实证明,我还是too young too simple ,sometimes naive...
实际开发工作中,react多页面应用的脚手架GitHub上有很多,自己也可以根据官方的react脚手架就可以愉快的撸起来,但是目前遇到的比较头疼的问题是,怎么让我的react应用代码压缩模块打包,这个时候就很有必要好好研究下webpack的使用了。
此次分享主要是针对多页面的webpack4打包。
So,咱们来聊一下,什么是webpack?
官网上的webpack相信所有前端开发童鞋都见过,webpack做的事情,就是将我们的js应用的各个文件按照模块打包成多个bundle,是一个静态模块的打包器。
webpack做的事情,就是分析项目结构,找到js模块以及一些不能直接运行的语言,并将其打包成合适的格式给浏览器执行。
安装webpack
npm install webpack-cli
了解webpack前需要知道webpack的四大核心概念,entry、output、loader、plugins。
Entry(入口)
entry是webpack的起点指示,告诉webpack从哪里开始入手打包任务,用来指定入口,默认值是./src。
如果是做单页面程序,单个入口。
//单个入口的写法 entry: string|Array<string>
entry:{
main:'../src/component/my.js'
}
entry:'../src/component/my.js'
//如果传数组形式,是注入多个入口以及多个依赖文件。
如果是做多个页面,多个入口。
//多个入口的写法,所有的文件会被打包到dist文件中。
entry:{
app:['./a.js','./b.js','./c.js']
}
//最终输出 dist/a.js / dist/b.js dist/c.js
当webpack在解析代码时,每遇到import或者require引入的依赖,最终会被打包在最终构建结果中。打包的最终结果只会引入该模块引入的依赖。
output(输出)
打包完后输出的位置
output: {
filename: 'vendor.js',
path: '/home/index'
}
loader(加载器)
实际上webpack只能打包js文件,其他资源例如css和html是需要加载器来将资源转化,加载进来。
举个栗子,如果项目中使用了sass的样式语法,是无法被浏览器所识别的,因此需要引用loader,将sass转义成能被识别的css语法阅读。
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
在该项目中,react用的是jsx的语法,同样无法被浏览器所识别,需要引用babel将jsx语法转义成正常的js运行。
module:{
rules:[
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",//可以根据配置的目标浏览器或者运行环境来自动将2015+的代码转为es5
"@babel/preset-react",
{ "plugins": ["@babel/plugin-proposal-class-properties"] }//这句可以在项目中使用箭头函数
],
}
},
]
}
loader的职责是单一的,一个loader相当于一个翻译官,只做翻译某种语言,需要关心输入和输出。
一个项目里有多个翻译官,在调用多个 Loader 去转换一个文件时,每个 Loader 会链式的顺序执行, 第一个 Loader 将会拿到需处理的原内容,上一个 Loader 处理后的结果会传给下一个接着处理,最后的 Loader 将处理后的最终结果返回给 Webpack。
plugins(插件)
常用的插件有
- webpack-dev-server
- copy-webpack-plugin
- uglifyjs-webpack-plugin
- webpack-spritesmith
- clean-webpack-plugin
- html-webpack-plugin
...还有好多好多
webpack-dev-server
非常常见,用于启动本地开发模式,本身是一个express服务器,封装了webpack-hot-middleware,实现了热更新。
devServer:{
host:'0.0.0.0' || 'localhost',
post:9000,//打开的端口
open:true,//自动打开
contentBase:path.join(_dirname,'src')//不设置的话,默认是当前执行的目录,一般是项目根目录,会在项目根目录查找index.html文件。
}
copy-webpack-plugin
在webpack相中拷贝文件或文件夹的方法。
new CopyWebpackPlugin({
from:'.....',//需要拷贝的源目标文件
to:'',//拷贝后存放的文件位置
})
uglifyjs-webpack-plugin
用来压缩优化js文件,webpack4+以上的版本可使用。
webpack4之前的版本是通过webpack.optimize.commonsChuckPlugin来压缩js
module.exports={
optimization:{
minimizer:true,//默认为true,效果就是压缩js代码
splitChuncks:{
chunks:'async',//'async':分割异步打包的代码,'all':同时分割同步和异步代码
cacheGroups:{//默认的规则不会打包,需要单独定义
vendors:{
test:/[\\/]node_modules[\\/]/,
name:'vendors'
},
commons:{
test:/commons\.js/,
name:'commons'
}
}
}
}
}
webpack-spritesmith
把零散的小图生成一张雪碧图,减少http请求
new SpritesmithPlugin({
// 目标小图标
src: {
cwd: `src/assets/images/sprites`,
glob: '*.png'
},
// 输出雪碧图文件及样式文件7
target: {
image: `dist/static/images/sprite-[hash].png`,
css: [
[
`dist/static/css/sprite.css`, {
formatOpts: {
cssSelector: (groupName) => `.icon-${groupName.name}` // 修改生成的sprite css中类名定义
}
}
]
]
},
// 样式文件中调用雪碧图地址写法
apiOptions: {
cssImageRef: '../images/sprite-[hash].png'
},
spritesmithOptions: {
algorithm: 'top-down'
}
})
clean-webpack-plugin
用来清除文件
// 删除文件 保留新文件
new CleanWebpackPlugin(['dist']),
html-webpack-plugin
为入口文件html加载js依赖,动态添加编译后的hash值文件,防止引用缓存文件的问题。
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({ // 打包输出HTML
minify: { // 压缩HTML文件
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true// 压缩内联css
},
filename: 'a.html',
template: 'a.html'
}),
new HtmlWebpackPlugin({ // 打包输出HTML
minify: { // 压缩HTML文件
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true// 压缩内联css
},
filename: 'b.html',
template: 'b.html'
}),
]
项目是多页的情况下,传入的html入口文件设置多个即可。
可以写一个提取html的函数,批量获取,组合成数组形式。
resolve配置
webpack在启动后会从配置的入口模块出发去找寻所有依赖的模块,resolve配置webpack如何找寻模块所对应的文件。
alias
resolve.alias配置项通过设置其他名字来将原路径映射成新的路径
resolve:{
extensions: ['.js', '.json','.css'],//导入的语句中如果没带文件后缀名,webpack会自动带上后缀名去尝试文件是否存在,用于配置在尝试过程中用到的后缀列表。
alias:{
'api':utils.resolve('/src/common/api/'),//通过api关键字替换'/src/common/api/'
}
}
总结
归结webpack在实际项目中做了哪些事情?
1、webpack从context设置的文件位置开始找寻
2、寻找entry里的所有文件名
3、在js文件编译过程中,遇到 import | require 引入的依赖文件,然后在依赖文件递归找寻所有依赖,最终打包在最终编译生成的文件。
4、webpack把所有生成的文件都输出到output.path路径中,以output.filename对应命名的模块来命名。
webpack之所以强大,是在于插件各式各样,总能满足你的需求。webpack4的新api大大的提高了Code Splitting 的体验,以上讲的内容还是浅显了点,具体的一些细节还是需要靠同志们自己愉快的撸起来,只有实操才是检验能力的唯一途径~
最后,po上项目中的webpack相关依赖的版本情况
{
"@babel/core": "^7.1.2",
"@babel/plugin-proposal-class-properties": "^7.2.1",
"@babel/plugin-transform-runtime": "^7.3.4",
"@babel/preset-env": "^7.1.0",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.3.4",
"babel-loader": "^8.0.4",
"babel-preset-env": "^1.7.0",
"clean-webpack-plugin": "^2.0.0",
"copy-webpack-plugin": "^5.0.3",
"css-loader": "^1.0.0",
"file-loader": "^2.0.0",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"image-webpack-loader": "^5.0.0",
"jsx-loader": "^0.13.2",
"postcss-loader": "^3.0.0",
"sass-loader": "^7.1.0",
"script-loader": "^0.7.2",
"style-loader": "^0.23.0",
"uglifyjs-webpack-plugin": "^2.0.1",
"url-loader": "^1.1.2",
"webpack": "^4.20.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.9",
"webpack-spritesmith": "^0.5.4",
"clean-webpack-plugin": "^2.0.0",
"copy-webpack-plugin": "^5.0.3",
}