1.webpack 命令
官方解释:webpack是一个现代的JavaScript应用的静态模块打包工具。
(1)使用全局webpack
webpack4以上版本 webpack-cli --entry ./app.js --output build.js
webpack4以下版本 webpack --entry ./app.js --output build.js
(2)使用本地webpack
- package.json文件中 增加下面命令,打包会优先使用项目中的webpack打包。使用默认配置文件 webpack.config.js文件进行打包。
"build": "webpack" //npm run build
- 指定配置文件,环境等等。
"build": "webpack webpack --config --webpack.common.js --env development"
- output配置
output.chunkFilename 配置无入口的 Chunk 在输出时的文件名称。 chunkFilename和上面的filename 非常类似,但chunkFilename只用于指定 在运行过程中生成的Chunk在输出时的文件名称。会在运行时生成 Chunk的常见场景包括:使用CommonChunkPlugin、使用 import(‘path/to/module’)动态加载等。chunkFilename支持和filename一 致的内置变量。
2. 编译js
ECMAScript 6.0(简称 ES6)是 JavaScript 语言的下一代标准。它在语言层面为 JavaScript 引入了很多新语法和 API ,使得 JavaScript 语言可以用来编写复杂的大型应用程序。例如:
- 规范模块化;
- Class 语法;
- 用 let 声明代码块内有效的变量 ,用 const 声明常量;
- 箭头函数;
- async 函数;
- Set 和 Map 数据结构。
-通过这些新特性,可以更加高效地编写代码,专注于解决问题本身。但遗憾的是不同浏览器对这些特性的支持不一致,使用了这些特性的代码可能会在部分浏览器下无法运行。为了解决兼容性问题,需要把 ES6 代码转换成 ES5 代码,Babel 是目前解决这个问题最好的工具。 Babel 的插件机制让它可灵活配置,支持把任何新语法转换成 ES5 的写法。
(1)编译器 babel-loader @babel/core
- 把es6编译成es5语法可以使用 babel-loader , 而 babel-loader是利用 @babel/core来进行编译的。所以需要安装两个:
npm install babel-loader @babel/core --save-dev
webpack.config.js文件简单配置
const path = require('path');
module.exports = {
entry: {
app: './index.js',
},
output: {
path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist')
filename: '[name].[hash:6].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
}
}
- 一个简单的文件结构目录
(2)编译规范 @babel/preset-env
- 指定编译规范
const path = require('path');
module.exports = {
entry: {
app: './index.js',
},
output: {
path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist')
filename: '[name].[hash:6].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets:[
['@babel/preset-env', {
targets: {
browsers: ['>1%'] // 编译成全球占有率大于1%的浏览器都可以识别
}
}]
]
}
}
}
]
}
}
(3)编译es6方法
- babel-loader只能编译语法,对于一些es6的方法是无能为力的。这就需要借助于一些插件。
npmi babel-polyfill babel-plugin-transform-runtime babel-runtime --save-dev
babel-polyfill
- babel-polyfill 生成一个全局对象,原理是把所有的es6的方法用es5的方法实现一遍,。 这样的话就会增大文件的体积。
- 入口文件直接引入
import 'babel-polyfill'
(function() {
new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1)
})
})
})()
- 也可以通过这样的方式引入 webpack的入口选项配置里使用
entry: ['babel-polyfill', './index.js'],
babel-plugin-transform-runtime
- 这是 Babel 官方提供的一个插件,作用是减少冗余的代码。Babel 在将 ES6 代码转换成 ES5 代码时,通常需要 一些由 ES5 编写的辅助函数来完成新语法的实现,例如在转换class extent语法时会在转换后的ES5代码里注入_extent辅助函数用于实现继 承:
function _extent(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
}
- 这会导致每个使用 class extent 语法的文件都被注入重复的_extent 辅助函数代码,babel-plugin-transform-runtime的作用在于将原本注入 JavaScript文件里的辅助函数替换成一条导入语句:
var _extent = require('babel-runtime/helpers/_extent');
- 这样能减小Babel编译出来的代码的文件大小。 需要个@babel/runtime 配置使用:
npm i @babel/plugin-transform-runtime @babel/runtime --save
修改options的配置
"options": {
"presets":[
["@babel/preset-env", {
"targets": {
"browsers": [">1%"] // 编译成全球占有率大于1%的浏览器都可以识别 node
}
}]
],
"plugins": [
["@babel/plugin-transform-runtime"]
]
}
(4)TypeScript
- 安装TypeScript和ts-loader
- 在配置文件中写入ts-loader
- tsconfig.json中配置
3 css编译
webpack是以js文件为入口文件打包的,那么项目的css怎么办?如何引入css?webpack会把所有的文件当作模块,包裹css文件,所以css文件需要引入到入口文件中。
css可以通过js引入但是必须使用loader
- css-loader,读取css文件
- style-loader,把css文件注入到js文件中,让css被引入后可以被正确的以一个style标签插入页面
- 两个的循序很重要,现过css-loader,交给style-loader
npm install --save css-loader style-loader
(1) style-loader
属性:
- insertAt: style标签插入在哪一块区域
- insertInfo: 插入指定的dom
- singleton: 是否合并为一个style标签
- transform: 在浏览器环境下,插入style到页面前,使用js对css进行操作
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
options: {
insertAt: 'top', // 'bottom' 相对于头部(只在头部区域)的top和bottom,
before: '#mydiv' // style标签插入的位置
insertInfo: '#mydiv', // style标签插入到哪个区域
singleton: true, // 是否合并为一个style标签
transform: './transform.js' // 用js对css修改
}
},
{
loader: 'css-loader',
}
]
}
transform.js
module.exports = function(css) {
console.log(window)
if(window.screen.width < 500) {
css = css.replace('red', 'yellow')
}
return css
}
(2) css-loader
{
loader: 'css-loader',
options: {
// modules: true, // css模块化
// webpack的写法
modules: {
localIdentName: '[path][name]_[local]_[hash:4]'
},
// webpack3的写法
// modules: true,
// localIdentName: '[path][name]_[local]_[hash:4]'
}
}
(3) less和sass css的预处理语言
module.exports = {
module: {
rules: [
{
// 增加对 SCSS 文件的支持
test: /\.scss$/,
// SCSS 文件的处理顺序为先 sass-loader 再 css-loader 再 style-loader
use: ['style-loader', 'css-loader', 'sass-loader'],
},
]
},
};
以上配置通过正则/.scss/匹配所有以.scss 为后缀的 SCSS 文件,再 分别使用 3个Loader去处理。具体处理流程如下。
- 通过sass-loader将SCSS源码转换为CSS代码,再将CSS代码交给 css-loader处理。
- ·css-loader 会找出 CSS 代码中@import 和 url()这样的导入语 句,告诉 Webpack依赖这些资源。同时支持CSS Modules、压缩CSS等 功能。处理完后再将结果交给style-loader处理。
- style-loader会将CSS代码转换成字符串后,注入JavaScript代码中,通过JavaScript向 DOM 增加样式。如果我们想将 CSS 代码提取到 一个单独的文件中,而不是和JavaScript混在一起,则可以使用在1.5节 中介绍过的ExtractTextPlugin
(4) 把css文件提取为单独的文件
webpack3 npm i extract-text-webpack-plugin --save
webpack4 npm i extract-text-webpack-plugin@next --save
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist')
filename: '[name].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
"options": {
"presets":[
["@babel/preset-env", {
"targets": {
"browsers": [">1%"] // 编译成全球占有率大于1%的浏览器都可以识别 node
}
}]
],
"plugins": [
["@babel/transform-runtime"]
]
}
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
// fallback: "style-loader",
fallback: {
loader: 'style-loader',
options: {
insertAt: 'top', // 'bottom' 相对于头部(只在头部区域)的top和bottom, style标签插入的位置 {before: '#mydiv'}
// insertInfo: '#mydiv', // style标签插入到哪个区域
singleton: true, // 是否合并为一个style标签
transform: './transform.js' // 用js对css修改
}
},
// use: "css-loader",
use: {
loader: 'css-loader',
options: {
// modules: true, // css模块化
// webpack的写法
modules: {
localIdentName: '[path][name]_[local]_[hash:4]'
},
// webpack3的写法
// modules: true,
// localIdentName: '[path][name]_[local]_[hash:4]'
}
}
}),
}
]
},
plugins: [
// new ExtractTextPlugin("styles.css"),
new ExtractTextPlugin({
filename: '[name].min.css'
}),
]
// devServer: {
// contentBase: "/dist",
// // hot:true,
// host: "0.0.0.0",
// compress: true,
// port: 9000,
// overlay: true,
// hotOnly: true // 只用热更新 不用reload
// },
// plugins: [
// new HtmlWebpackPlugin({
// filename:'index.html',
// template : './index.html',
// minify:{
// removeComments:true, //删除注释
// collapseWhitespace: true //删除空格,压缩
// }
// })
// ]
};
(5)postcss-loader
因为要兼容不同的浏览器,需要对css文件加前缀做一些兼容性处理,手机端的还需要把px转换成rem自适应。这里需要用到一个工具postcss-loader。
postcss 一种对css编译的工具,类似babel对js的处理,常见的功能如:
- 使用下一代css语法
- 自动补全浏览器前缀
- 自动把px代为转换成rem(rem不熟悉的,点这)
- css 代码压缩等等
postcss 只是一个工具,本身不会对css操作,它通过插件实现功能,autoprefixer 就是其一。
npm i postcss postcss-loader autoprefixer postcss-cssnext postcss-pxtorem --save
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist')
filename: '[name].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
"options": {
"presets":[
["@babel/preset-env", {
"targets": {
"browsers": [">1%"] // 编译成全球占有率大于1%的浏览器都可以识别 node
}
}]
],
"plugins": [
["@babel/transform-runtime"]
]
}
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
// fallback: "style-loader",
fallback: {
loader: 'style-loader',
options: {
insertAt: 'top', // 'bottom' 相对于头部(只在头部区域)的top和bottom, style标签插入的位置 {before: '#mydiv'}
// insertInfo: '#mydiv', // style标签插入到哪个区域
singleton: true, // 是否合并为一个style标签
transform: './transform.js' // 用js对css修改
}
},
// use: "css-loader",
use: [
{
loader: 'css-loader', // 让js文件识别css文件
options: {
// modules: true, // css模块化
// webpack的写法
modules: {
localIdentName: '[path][name]_[local]_[hash:4]' // 模块化指定class名称
},
// webpack3的写法
// modules: true,
// localIdentName: '[path][name]_[local]_[hash:4]'
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('autoprefixer')({
"overrideBrowserslist": [
'>1%', 'last 2 versions' // 指定兼容的浏览器目标,市场占有率大于1%
]
})
]
}
}
]
}),
}
]
},
plugins: [
// new ExtractTextPlugin("styles.css"),
new ExtractTextPlugin({
filename: '[name].min.css'
}),
]
// devServer: {
// contentBase: "/dist",
// // hot:true,
// host: "0.0.0.0",
// compress: true,
// port: 9000,
// overlay: true,
// hotOnly: true // 只用热更新 不用reload
// },
// plugins: [
// new HtmlWebpackPlugin({
// filename:'index.html',
// template : './index.html',
// minify:{
// removeComments:true, //删除注释
// collapseWhitespace: true //删除空格,压缩
// }
// })
// ]
};
index.css文件中增加 display: flex, 打包后,自动增加了前缀
也可以在package.json文件中加入"browserslist": [ “>1%”, “last 2 versions” ]
在 PostCSS 启动时,会从目录下的 postcss.config.js 文件中读取所需配置,如果没有,再去webpack中找,所以也可以新建该文件,文件内容大致如下:
module.exports = {
plugins: [
// 需要使用的插件列表
require('postcss-cssnext')
]
}
4 打包html文件
npm i html-webpack-plugin --save
(1)options
- filename: 打包生成后的html文件的名称
- template: 指定一个html为模版
- minify: 压缩html
- inject: 是否把js,css文件插入到html
- chunks: 多入口时,指定需要引入的chunks
该插件将为你生成一个 HTML5 文件, 其中包括使用 script 标签的 body 中的所有 webpack 包。 只需添加插件到你的 webpack 配置如下:
plugins: [
// new ExtractTextPlugin("styles.css"),
new ExtractTextPlugin({
filename: '[name].min.css'
}),
// 处理html文件 打包压缩
new HtmlWebpackPlugin({
filename:'index.html',
template : './index.html',
minify:{ // 压缩
removeComments:true, //删除注释
collapseWhitespace: true //删除空格,压缩
}
})
]
打包发现会报错,webpack4需要这样安装插件 npm i html-webpack-plugin@next --save 这样打包就没有问题了
(2)chunks
chunks主要用于多入口文件,当你有多个入口文件,那就会编译后生成多个打包后的文件,那么chunks 就能选择你要使用那些js文件
entry: {
index: path.resolve(__dirname, './src/index.js'),
devor: path.resolve(__dirname, './src/devor.js'),
main: path.resolve(__dirname, './src/main.js')
}
plugins: [
new HtmlWebpackPlugin({
chunks: ['index','main']
})
]
5 webpack的环境
因为在不同的场景下可能需要不同的配置,使用不同的功能,所以需要区分环境:
- 开发模式:会额外用到一些调试功能,比如webpack-dev-server, 但是为了加快调试速度,可能不会压缩、three-shaking之类的功能
- 生成模式:为了减少文件体积,会去压缩,three-shaking等功能,但不需要webpack-dev-server或者eslint这样的调试工具。
production | development |
---|---|
去除无用代码 | webpack-dev-server |
图片压缩,转base64 | source-map |
提取公用代码 | 代码风格检查 |
等等 | 等等 |
"build": "webpack --env development" // 打包的时候告诉webpack是什么环境
把webpack.config.js调整为暴露一个函数, 主要内容不变
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = env => {
console.log(env)
const common = {
mode: 'development',
entry: './index.js',
output: {
path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist')
filename: '[name].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
"options": {
"presets":[
["@babel/preset-env", {
"targets": {
"browsers": [">1%"] // 编译成全球占有率大于1%的浏览器都可以识别 node
}
}]
],
"plugins": [
["@babel/transform-runtime"]
]
}
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
// fallback: "style-loader",
fallback: {
loader: 'style-loader',
options: {
insertAt: 'top', // 'bottom' 相对于头部(只在头部区域)的top和bottom, style标签插入的位置 {before: '#mydiv'}
// insertInfo: '#mydiv', // style标签插入到哪个区域
singleton: true, // 是否合并为一个style标签
transform: './transform.js' // 用js对css修改
}
},
// use: "css-loader",
use: [
{
loader: 'css-loader', // 让js文件识别css文件
options: {
// modules: true, // css模块化
// webpack的写法
modules: {
localIdentName: '[path][name]_[local]_[hash:4]' // 模块化指定class名称
},
// webpack3的写法
// modules: true,
// localIdentName: '[path][name]_[local]_[hash:4]'
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('autoprefixer')()
]
}
}
]
}),
}
]
},
plugins: [
// new ExtractTextPlugin("styles.css"),
new ExtractTextPlugin({
filename: '[name].min.css'
}),
// 处理html文件 打包压缩
new HtmlWebpackPlugin({
filename:'index.html',
template : 'index.html',
minify:{ // 压缩
removeComments:true, //删除注释
collapseWhitespace: true //删除空格,压缩
},
inject: true, // 是否自动引入 默认true true/false
})
]
// devServer: {
// contentBase: "/dist",
// // hot:true,
// host: "0.0.0.0",
// compress: true,
// port: 9000,
// overlay: true,
// hotOnly: true // 只用热更新 不用reload
// }
}
return common;
};
tnpm run build 打包后看到控制台可以打印 出development
因为生产环境和开发环境需要做的不一,所以我们就需要编写两套webpack配置。
创建webpack.dev.js文件‘
const webpack = require('webpack')
module.exports = {
devServer: {
// contentBase: "/dist",
hot:true,
host: "0.0.0.0",
compress: true,
port: 9000,
overlay: true,
// hotOnly: true // 只用热更新 不用reload
},
// plugins: [
// new webpack.HotModuleReplacementPlugin(),
// new webpack.NamedModulesPlugin()
// ]
};
创建webpack.pro.js文件
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
optimization :{
minimize: false
},
plugins: [
// 处理html文件 打包压缩
new HtmlWebpackPlugin({
filename:'index.html',
template : './index.html',
minify:{ // 压缩
removeComments:true, //删除注释
collapseWhitespace: true //删除空格,压缩
},
inject: true, // 是否自动引入 默认true true/false
})
]
}
这里把webpack.config.js文件改为了webpack.common.js文件 你也可以不用改这个名字。使用默认的就行
增加引入内容
const dev = require('./webpack.dev.js');
const pro = require('./webpack.pro.js');
const merge = require('webpack-merge')
return的不再是common而是合并后的
return merge(common, env == 'production' ? pro : dev)
修改package.json文件
"dev": "webpack-dev-server --env development --config webpack.common.js",
"build": "webpack --env production --config webpack.common.js",
webpack --env name 指定环境 --config 指定webpack配置文件
这样就可以执行不同的命令,不同的环境下完成不同的任务。
6 devtool
7 图片资源的加载
(1)file-loader
{
test:/\.(png|jpg|jgeg|gif)$/,
use:[
{
loader:'file-loader',
options: {
name: "[name].[hash:4].[ext]",
outputPath: "assets/img", // outputPath: "assets/img",可以让我们指定文件夹。
publicPath: 'aaa'
}
},
],
}
publicPath
(2)url-loader
url-loader是file-loader的二次封装,我们也可以把file-loader直接换成url-loader。一般我们都会用url-loader。增加了一些新的功能,最核心的就是base64功能。把一些资源转化成base64,好处就是减少一些http请求。
简单说下工作中遇到的问题吧,我们做的一个项目中首页用了十多张图片,每张图片都是一个静态资源,所以都会有http请求,为了减少请求,我们可以通过base64编码的方法来展示图片。webpack中有一个包叫做url-loader,他可以将html以及css中的图片打包成base64,但是js中如果有图片url并不能编译成功
{
test:/\.(png|jpg|jgeg|gif)$/,
use:[
{
loader:'url-loader',
options: {
name: "[name].[hash:4].[ext]",
outputPath: "assets/img",
publicPath: 'aaa',
limit: 30 * 1024
}
},
],
}
limit: 3000,只对30kb以下的图片转码成base64。 因为转码会增大文件体积,所以不会对所有图片进行转码,会影响加载速度
可以使用如下配置做适配:
module.exports = {
module: {
rules: [
{
test: /\.png$/,
use: [{
loader: 'url-loader',
options: {
// 30KB 以下的文件采用 url-loader
limit: 1024 * 30,
// 否则采用 file-loader,默认值就是 file-loader
fallback: 'file-loader',
}
}]
}
]
},
};
还可以使用图片压缩来优化,img-loader图片压缩 本身没有压缩功能,是由一系列的插件来做的
{
loader:'img-loader',
options: {
plugins: [
require('imagemin-gifsicle')({ // 压缩gif
interlaced: false,
optimizationLevel: 3, // 1, 2, 3 3是极限压缩,体积很小 质量很好
}),
require('imagemin-mozjpeg')({ // 压缩jpeg图片
quality: 80, // 1-100 图片质量 越大质量越好
progressive: true,
arithmetic: false
}),
require('imagemin-pngquant')({ // png图片的压缩
floyd: 0.5,
speed: 2 // 1-11 越大压缩的的体积越大,跟质量成正比
}),
require('imagemin-svgo')({
plugins: [
{ removeTitle: true },
{ convertPathData: false }
]
})
]
}
},
如果要在html中引入图片,需要以下面的方式引入,webpack才会识别处理
<img src="${require('./assets/img/img4.jpg')}"/>
(3)html-loader
使得html中引入的文件也可以被html识别
{
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
attrs: ["img:data-src"] // 或者["img":src] 就会对src属性处理
}
}
},
然后这样引入图片
<img data-src="./assets/img/img4.jpg"/>