webpack 4x
中文文档:https://www.webpackjs.com/concepts/
网页中的静态资源
- js : .js .jsx(react).coffee .ts(Typescript 类 c#语言)
- css: css .less .sass(基本没用了) .scss
- Images
- 字体模板
- 模板文件 : .ejs .jsde .vue
静态资源多了有什么问题?
- 网页加载速度慢, 因为 我们要发起很多的二次请求;
- 要处理错综复杂的依赖关系
如何解决上述两个问题
- 合并、压缩、精灵图、图片的Base64编码
- 可以使用webpack可以解决各个包之间的复杂依赖关系;
压缩合并工具:
- gulp是基于task任务的 (小乔,灵活,便于小项目的构建)
- webpack 基于整个项目构造()
简介
webpack是前端的一个项目构建工具,它基于Node.js开发出来的一个前端工具
注意
es6语法浏览器无法识别,得降低版本
能做什么?
- 能处理js文件的互相依赖关系
- 能处理js的兼容问题,把高级的、浏览器不识别的语法,转为低级的,浏览器能正常识别的语法
安装webpack
先全局安装
- 安装:
npm install -g webpack
- 安装脚手架:
npm install -g webpack-cli
- 也可以一次安装两个:
yarn add webpack webpack-cli
- 检查版本号(没有版本号代表安装失败):
webpack -v
再局部安装
- 安装:
npm install webpack -D
- 安装脚手架:
npm install webpack-cli -D
- 也可以一次安装两个:
yarn add webpack webpack-cli -D
(-D就是--save-dev 。-S就是--save)
动手:
最简单的打包,使用默认0配置,啥都不配置。
- 新建一个项目文件,
yarn init -y
,初始化默认包配置文件 - 局部安装webpack
yarn add webpack webpack-cli -D
- 新建src文件夹,里边新建一个index.js,随便编写脚本文件
npx webpack
,发现会有一个dist目录生成,里边有一个压缩过的main.js- 在src和dist中分别创建一个index.html引入各自的js。分别在浏览器中打开。
知识点:
-
npx是npm v5.2引入的一个命令,npx 会自动查找当前依赖包中的可执行文件,如果找不到,就会去 PATH 里找。如果依然找不到,就会帮你安装!
-
压缩过的js会多出很多代码,为什么?
- 打包,默认支持js的模块化,多出来的很多东西是关于模块化的兼容
-
dist中html在浏览器中能正常输出,src中的html却会报错‘require is not defined’,为什么?
- dist中的打包出来的代码,模块化相关的东西是经过兼容处理的,所以不会报这个错
- src里边的代码没有引入模块化相关的js文件,所以会报错。
- 但是在命令行中用node执行是不会报错的,因为node环境中有模块化相关的js文件
手动配置webpack
默认webpack的配置文件时webpack.config.js。名字是死的,
webpack-cli/bin/config-yargs.js中搜索webpack.config.js,默认有两个名字可以用
出口入口和模式
let path = require('path');//模块用于处理文件与目录的路径
module.exports = {
mode:'development',//打包模式:开发或生产
entry:'./src/01.js',//目标文件
output:{
filename:'bundle.js',//输出的名字
path:path.resolve(__dirname,'build')//必须是绝对路径:
//publicPath:'https://static2.yscase.com/'//把所有路径都加上cdn前缀地址
//node的path对象的resolve方法可以把当前路径转为绝对路径
//__dirname:当前模块的目录名
//参考说明:http://nodejs.cn/api/path.html#path_path_resolve_paths
}
}
自定义的配置文件名字
如:webpack.config.my.js
执行时,要这样执行:webpack --config webpack.config.js
配置package.json脚本
如果嫌弃命令语句太长,可以在package.json里添加脚本语句
xxxxxxxxxx
"scripts":{
"build":"webpack --config webpack.config.my.js"
},
执行yarn run build
webpack-dev-server
启动一个服务器,在服务器上预览
-
安装
yarn add webpack-dev-server -D
-
配置
xxxxxxxxxx
devServer:{
progress:true,
contentBase:'./build',//指定文件夹
port:3000,//设置端口号
compress:true,//压缩
},
-
添加到脚本
xxxxxxxxxx
"scripts": {
"dev": "webpack-dev-server"
}
-
执行yarn run dev
html打包
html-webpack-plugin(插件)
会自动引入js
普通单页面
-
安装插件
yarn add html-webpack-plugin -D
-
配置package,添加脚本
xxxxxxxxxx
"scripts": {
"build": "webpack",
},
-
配置插件(你的第一个插件)
-
先在顶部引入
xxxxxxxxxx
let HtmlWebpackPlugin = require('html-webpack-plugin');//打包html
-
xxxxxxxxxx
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html',//要打包的html
filename:'index.html',//输出的文件名
minify:{
removeAttributeQuotes:true,//删除双引号
collapseWhitespace:true,//删除换行和空格
},
hash:true//hash随机数,给引用文件添加版本号
})
]
-
-
压缩html
xxxxxxxxxx
new HtmlWebpackPlugin({
minify:{
removeAttributeQuotes:true,//删除双引号
collapseWhitespace:true,//删除换行和空格
},
})
-
hash戳解决缓存问题
-
给引用文件添加版本号
xxxxxxxxxx
new HtmlWebpackPlugin({
hash:true//hash随机数,给引用文件添加版本号
})
-
文件名添加hash
xxxxxxxxxx
output:{
filename:'bundle.[hash:8].js',//输出的名字
},
//加:8代表只显示8位hash值
-
-
执行
yarn run build
多页应用打包
有各自的html和入口js文件
xxxxxxxxxx
const Path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode:'development',
entry:{ //多页时,写成对象模式,起个名字
home:'./src/index.js',
share:'./src/share.js'
},
output:{
filename:'[name].js', //此时name为入口时起的名字
path:Path.resolve(__dirname,'dist')
},
plugins:[
//HtmlWebpackPlugin插件有几个页面就实例化几个
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['home']//指定入口文件
}),
new HtmlWebpackPlugin({
template:'./src/share.html',
filename:'share.html',
chunks:['share']//指定入口文件
})
]
}
样式打包
打包css,less
打包css,less(less-loader),sass等
-
安装依赖
yarn add css-loader style-loader -D
-
webpack‘配置文件,
xxxxxxxxxx
module:{
rules: [
{
test: /.css$/,
use: [
{ loader: "style-loader" },//写成对象时可以配置参数
"css-loader" ,//直接写字符串不能配置参数
],
}
],
}
-
css-loader:解决@import
-
style-loader:把css内容嵌入html
-
注意,use数组元素执行顺序是后往前,先css-loader,再style-loader
-
如是less
x{
test: /.less$/,
use: [
"style-loader",
"css-loader" ,
"less-loader" ,
]
}
分离css
-
安装mini-css-extract-plugin插件
yarn add mini-css-extract-plugin -D
-
是插件,就要在顶部引入
xxxxxxxxxx
let MiniCssExtractPlugin = require('mini-css-extract-plugin');//分离css
-
配置插件参数
xxxxxxxxxx
new MiniCssExtractPlugin({
//filename:'main.css',//输出的文件名
filename:'css/main.css',//输出到指定文件夹
})
-
把原来的style-loader替换成MiniCssExtractPlugin.loader,原来的style-loader用于把css嵌入html,现在不需要了,而是,MiniCssExtractPlugin会生成一个link标签且分理处一个css文件,在html中引用
xxxxxxxxxx
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
]
},
-
如是less文件的页同理
xxxxxxxxxx
{
test: /.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader" ,
"less-loader" ,
]
}
给css,less添加前缀
postcss-loader 用来处理css优化的 autoprefixer是其中一种优化:添加浏览器前缀
-
安装
yarn add postcss-loader autoprefixer -D
-
css-loader之前执行
xxxxxxxxxx
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",//一定放在css-loader后面,才会先执行
],
},
-
如是less
xxxxxxxxxx
use: [
MiniCssExtractPlugin.loader,
"css-loader" ,
"postcss-loader",//less-loader之后执行,css-loader之前执行
"less-loader" ,
],
-
添加postcss配置文件postcss.config.js
-
xxxxxxxxxx
module.exports = {
plugins:[
require('autoprefixer')
]
}
-
注意:请尽量在js中引入css或less,如果在css或less中引入css或less,@import导入的内容不会被自动添加浏览器前缀!
压缩css
把mode设置为production,webpack打包一下,会发现js会自动压缩,但css不会
-
安装所需要的模块
-
把mode设置为production生产模式
-
配置文件顶部引入模块
yarn add optimize-css-assets-webpack-plugin UglifyJsPlugin -D
xxxxxxxxxx
//压缩css
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
//用于压缩js,如果要压缩css就必须添加压缩js的,否则js将不会压缩,
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
-
配置参数
xxxxxxxxxx
optimization: {
minimizer: [
//压缩js
new UglifyJsPlugin({
cache: true,//缓存
parallel: true,//是否并发打包
sourceMap: true // 高级js 转es5会用到 source maps
}),
//压缩css
new OptimizeCSSAssetsPlugin({})
]
},
-
执行
yarn run build
查看build文件夹中js和css是否都是压缩的
图片资源打包
file-loader
官网https://webpack.js.org/loaders/file-loader/
html-withimg-loader
js中引用的图片,css中的背景图片,html中的img标签图片,
file-loader匹配出找所有的图片,创建一个新的图片到指定目录,默认会生成新的带hash值的名字
-
安装本地依赖
yarn add file-loader -D
-
配置loader
xxxxxxxxxx
{
test: /.(png|jpg|gif)$/,
use: 'file-loader'
},
-
读取图片
-
js中的图片
-
需要通过引入(import或require),才会被打包成新的图片
xxxxxxxxxx
import girl from './girl.jpg';//引入图片
console.log(girl);//新的图片路径
let img = new Image();
img.src = girl;
document.body.appendChild(img);
-
-
css中的背景图片
- 使用了css-loader的话,css中的背景图片都能显示正常,因为css的路径都会被转为
url(require("./logo.png"))
,这样就会主动去生成新的路径并直接返回了
- 使用了css-loader的话,css中的背景图片都能显示正常,因为css的路径都会被转为
-
html中的图片
-
默认html中的图片不会被打包,因为没有被依赖,没有通过引入(import或require)
-
我们需要安装一个新的loader,用来解决这个问题
yarn add html-withimg-loader -D
-
在loader中添加规则
xxxxxxxxxx
{
test: /.html$/,
use: 'html-withimg-loader'
},
-
-
-
打包执行
yarn run build
输出到指定文件夹
xxxxxxxxxx
{
test: /.(png|jpg|gif)$/,
use: {
loader:'file-loader',
options:{
limit:1,//小于最小体积就转为base64
outputPath:'img/',//指定文件夹
name:'[name]-[hash:6].[ext]',//名字
//publicPath:'https://static2.yscase.com/'//把所有图片都加上cdn前缀地址
}
}
}
高级js转es5
babel官网:https://babeljs.io/docs/en/
配合官网文档,更细致,在打包报错时,会有报错提示,看提示是缺少什么插件(包/模块),我们就去官网上搜索,查看使用
基本的es6
@babel/core是babel-loader的核心,
@babel/preset-env处理转化
声明语句,箭头函数,class类
babel-loader
@babel/core
@babel/preset-env
-
安装
yarn add babel-loader @babel/core @babel/preset-env -D
-
添加规则
xxxxxxxxxx
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,//匹配时排除该文件夹
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'] //预设
}
}
}
class类静态属性
plugin-proposal-class-properties
https://babeljs.io/docs/en/next/babel-plugin-proposal-class-properties.html
-
安装针对class类的转化
yarn add @babel/plugin-proposal-class-properties -D
-
添加插件配置
xxxxxxxxxx
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins:[
'@babel/plugin-proposal-class-properties' //针对class
]
}
}
-
执行
yarn run build
装饰器
plugin-proposal-decorators
-
安装依赖
yarn add @babel/plugin-proposal-decorators -D
-
用法:https://babeljs.io/docs/en/babel-plugin-proposal-decorators注意babel插件plugins顺序
xxxxxxxxxx
options: {
presets: [
'@babel/preset-env'
],
plugins:[
["@babel/plugin-proposal-decorators", { "legacy": true }],//宽松模式
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}
transform-runtime
如果你用了generator函数,打包时没有报错,但是打包后的代码在浏览器中却是报了错regeneratorRuntime is not defined
此时你还需要安装@babel/plugin-transform-runtime,帮助转化的包
plugin-transform-runtime还可以优化代码,减少重复的代码
-
安装开发时的依赖
yarn add @babel/plugin-transform-runtime -D
-
安装生产上线时的依赖
yarn add @babel/runtime -S
-
添加到babel依赖插件中
xxxxxxxxxx
plugins:[
["@babel/plugin-proposal-decorators", { "legacy": true }],//宽松模式
["@babel/plugin-proposal-class-properties", { "loose" : true }],
"@babel/plugin-transform-runtime"
]
-
执行打包
yarn run build
更高级的语法
This means you can use new built-ins like
Promise
orWeakMap
, static methods likeArray.from
orObject.assign
, instance methods likeArray.prototype.includes
, and generator functions (provided you use the regenerator plugin).源自babel官网: https://babeljs.io/docs/en/babel-polyfill#docsNav
@babel/polyfill
-
安装生产上线时的依赖
yarn add @babel/polyfill -S
-
js入口顶部引入
require("@babel/polyfill");
import "@babel/polyfill";
-
执行打包,打包后的页面在ie中也能正常执行,没有使用这款插件的在低版本浏览器会报错的
ESLint高级js语法检验
暴露全局变量
require引入的是在当前模块,并不是全局,如要暴露给全局,如下
expose-loader
expose-loader暴露给全局(目前juqery已经是全局的了,再次只是比如)
-
安装expose-loader
yarn add expose-loader -D
-
使用
-
引入时设置变量
import $ from 'expose-loader?$!jquery'
-
webpack配置添加匹配规则
{ test:require.resolve('jquery'), //详解见http://nodejs.cn/api/modules.html#modules_require use:[{ loader:'expose-loader', options:'$'//暴露出来的全局变量 }] },
-
-
打包运行
ProvidePlugin
ProvidePlugin在每个js模块中引入插件
-
先引入
const Webpack = require('webpack')
-
再配置插件
new Webpack.ProvidePlugin({ $:'jquery', jquery:'juqery' })
-
打包运行
webpack常用配置
sourcemap源码映射
打包后的代码都被压缩了,浏览器上不好找到报错的地方,我们可以通过sourcemap找到报错的地方行和列
官网文档:https://webpack.js.org/configuration/devtool/#devtool
module.exports = { mode:'production', //源码映射,会单独生成一个sourcemap文件,浏览器调试时出错会标识列和行 devtool:'source-map', //源码映射,不会产生单独的文件,但是浏览器调试时可以显示行和列 devtool:'eval-source-map', // 不会产生列,但是是一个单独的映射文件 devtool:'cheap-module-source-map', }
watch
实时打包更新
常用插件
cleanWebpackPlugin
打包前清空文件夹https://www.npmjs.com/package/clean-webpack-plugin
-
先安装
yarn add clean-webpack-plugin -D
-
引入插件
const CleanWebpackPlugin = require('clean-webpack-plugin');
-
plugins:[ new CleanWebpackPlugin('./dist')//传入要清空的文件夹,也可以是数组 ],
copyWebpackPlugin
复制文件到指定目录
-
先安装
yarn add copy-webpack-plugin -D
-
引入插件
const CopyWebpackPlugin = require('copy-webpack-plugin');
-
plugins:[ new CopyWebpackPlugin([ {from:'./other',to:'./other'}//从根目录到输出目录 ]) ],
bannerPlugin 内置
给打包后的文件顶部嵌入标识语句,
-
webpack内置插件,引入webpack即可
const Webpack = require('webpack');
-
new Webpack.BannerPlugin({ banner:'2019.2.13by xiaoyue'//指定内嵌语句 })