webpack4学习
一.安装前先npm初始化
1 npm init -y 2 npm i webpack webpack-cli -D
创建webpack.config.js文件
let path = require('path') // 相对路径变绝对路径 module.exports = { mode: 'production', // 模式 默认 production development entry: './src/index', // 入口 output: { filename: 'bundle.[hash:8].js', // hash: 8只显示8位 path: path.resolve(__dirname, 'dist'), publicPath: '' // // 给所有打包文件引入时加前缀,包括css,js,img,如果只想处理图片可以单独在url-loader配置中加publicPath } }
二.本地服务
npm i webpack-dev-server -D
devServer: { port: 3000, progress: true // 滚动条 contentBase: './build' // 起服务的地址 open: true // 自动打开浏览器 compress: true // gzip压缩 }
三.处理html
npm i html-webpack-plugin -D
let HtmlWebpackPlugin = require('html-webpack-plugin') plugins: [ // 放着所有webpack插件 new HtmlWebpackPlugin({ // 用于使用模板打包时生成index.html文件,并且在run dev时会将模板文件也打包到内存中 template: './index.html', // 模板文件 filename: 'index.html', // 打包后生成文件 hash: true, // 添加hash值解决缓存问题 minify: { // 对打包的html模板进行压缩 removeAttributeQuotes: true, // 删除属性双引号 collapseWhitespace: true // 折叠空行变成一行 } }) ]
四.处理css
npm i css-loader style-loader -D
// css-loader 作用:用来解析@import这种语法 // style-loader 作用:把 css 插入到head标签中 // loader的执行顺序: 默认是从右向左(从下向上) module: { // 模块 rules: [ // 规则 // style-loader 把css插入head标签中 // loader 功能单一 // 多个loader 需要 [] // 顺便默认从右到左 // 也可以写成对象方式 { test: /\.css$/, // css 处理 // use: 'css-loader' // use: ['style-loader', 'css-loader'], use: [ // { // loader: 'style-loader', // options: { // insertAt: 'top' // 将css标签插入最顶头 这样可以自定义style不被覆盖 // } // }, MiniCssExtractPlugin.loader, 'css-loader', // css-loader 用来解析@import这种语法, 'postcss-loader' ] } ] }
五.处理less等
npm i less-loader
{ test: /\.less$/, // less 处理 // use: 'css-loader' // use: ['style-loader', 'css-loader'], use: [ // { // loader: 'style-loader', // options: { // insertAt: 'top' // 将css标签插入最顶头 这样可以自定义style不被覆盖 // } // }, MiniCssExtractPlugin.loader, // 这样相当于抽离成一个css文件, 如果希望抽离成分别不同的css, 需要再引入MiniCssExtractPlugin,再配置 'css-loader', // css-loader 用来解析@import这种语法 'postcss-loader', 'less-loader' // less-loader less -> css // sass node-sass sass-loader // stylus stylus-loader ] }
六.抽离css文件,通过link引入
yarn add mini-css-extract-plugin -D
let MiniCssExtractPlugin = require('mini-css-extract-plugin') // 压缩css plugins: [ new MiniCssExtractPlugin({ filename: 'css/main.css' }) ] { test: /\.css$/, // css 处理 // use: 'css-loader' // use: ['style-loader', 'css-loader'], use: [ // { // loader: 'style-loader', // options: { // insertAt: 'top' // 将css标签插入最顶头 这样可以自定义style不被覆盖 // } // }, // 此时不需要style-loader MiniCssExtractPlugin.loader, // 抽离 'css-loader', // css-loader 用来解析@import这种语法, 'postcss-loader' ] }
1.压缩css和js
// 用了`mini-css-extract-plugin`抽离css为link需使用`optimize-css-assets-webpack-plugin`进行压缩css,使用此方法压缩了css需要`uglifyjs-webpack-plugin`压缩js const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin") const UglifyJsPlugin = require("uglifyjs-webpack-plugin") module.exports = { optimization: { // 优化项 minimizer: [ new UglifyJsPlugin({ // 优化js cache: true, // 是否缓存 parallel: true, // 是否并发打包 // sourceMap: true // 源码映射 set to true if you want JS source maps }), new OptimizeCSSAssetsPlugin({}) // css 的优化 ] }, mode: 'production', entry: '', output: {}, }
七.给css加上兼容浏览器的前缀
yarn add postcss-loader autoprefixer -D
// css { test: /\.css$/, // css 处理 // use: 'css-loader' // use: ['style-loader', 'css-loader'], use: [ // { // loader: 'style-loader', // options: { // insertAt: 'top' // 将css标签插入最顶头 这样可以自定义style不被覆盖 // } // }, MiniCssExtractPlugin.loader, 'css-loader', // css-loader 用来解析@import这种语法, 'postcss-loader' ] } // less { test: /\.less$/, // less 处理 // use: 'css-loader' // use: ['style-loader', 'css-loader'], use: [ // { // loader: 'style-loader', // options: { // insertAt: 'top' // 将css标签插入最顶头 这样可以自定义style不被覆盖 // } // }, MiniCssExtractPlugin.loader, // 这样相当于抽离成一个css文件, 如果希望抽离成分别不同的css, 需要再引入MiniCssExtractPlugin,再配置 'css-loader', // css-loader 用来解析@import这种语法 'postcss-loader', 'less-loader' // less-loader less -> css // sass node-sass sass-loader // stylus stylus-loader ] },
postcss 需要配置文档 postcss.config.js
module.exports = { plugins: [ require('autoprefixer') ] }
八.es6 转 es5
npm i babel-loader @babel/core @babel/preset-env -D
module.exports = { module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [ //预设 '@babel/preset-env' ], plugins:[ // 转es7的语法 ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties", { "loose" : true }] ] } }, exclude: /node_modules/ } ] } }
1.转es7的语法
// 转class npm i @babel/plugin-proposal-class-properties -D // 转装饰器 npm i @babel/plugin-proposal-decorators -D
2.其他不兼容的高级语法
使用 @babel/polyfill
3.语法检查 eslint
npm i eslint eslint-loader -S
根目录添加 .eslintrc.json
配置文件
module.exports = { module: { rules: [ { test: /\.js$/, use: { loader: 'eslint-loader', options: { enforce: 'pre' // previous优先执行 post-普通loader之后执行 } } }, { test: /\.js$/, // mormal 普通的loader use: { loader: 'babel-loader', options: { presets: [ //预设 '@babel/preset-env' ] } }, exclude: /node_modules/ } ] } }
九.全局变量引入第三方库(jq)
npm i jquery -S
let webpack = require('webpack') new webpack.ProvidePlugin({ $: 'jquery' })
1.暴露全局
npm i expose-loader -D 暴露全局的loader
a.
可以在js中
import $ from 'expose-loader?$!jquery' // 全局暴露jquery为$符号
可以调用window.$
b.
也可在webpack.config.js
中配置 rules
module.exports = { module: { rules: [ { test: require.resolve('jquery'), use: 'expose-loader?$' } ] } }
以后在.js
文件中引入
import $ from 'jquery'
c.
如何在每个模块中注入:
let webpack = require('webpack') module.exports = { plugins: [ new webpack.ProvidePlugin({ $: 'jquery' }) ] } 之后代码内直接使用 $
d.
在index.html
中通过script
标签引入jquery
, 但是在js
中,用import
会重新打包jquery
,如何避免
从输出的bundle 中排除依赖
module.exports = { externals: { // 告知webpack是外部引入的,不需要打包 jquery: 'jQuery' } }
此时在index.js上
import $ from 'jquery'
console.log($)
十.webpack图片打包处理
1.js中创建
2.css中引入
3.<img src="">
yarn add file-loader -D 适合一二情况
module.export={ module: { rules: [ { test: /\.(png|jpg|gif)$/, use: 'file-loader' } ] } }
默认会内部生成一张图片到build,生成图片的路径返回回来
第一种情况: 图片地址要import
引入,直接写图片的地址,会默认为字符串
import logo from './logo.png' let image = new Image() image.src = logo document.body.appendChild(image)
第二种情况: css-loader
会将css
里面的图片转为require
的格式
div { background: url("./logo.png"); }
第三种情况: 解析html
中的image
{ test: /\.html$/, use: 'html-withimg-loader' }
yarn add html-withimg-loader -D
4. 当图片小于多少,用base64
yarn add url-loader -D 如果过大,才用file-loader
{ test: /\.(png|jpg|gif)$/, // 当图片小于多少,用base64,否则用file-loader产生真实的图片 use: { loader: 'url-loader', options: { limit: 200 * 1024, // 小于200k变成base64 // outputPath: '/img/', // 打包后输出地址 // publicPath: '' // 给资源加上域名路径 } }
十一.打包文件分类和多页面
1.图片
{ test: /\.(png|jpg|gif)$/, // 当图片小于多少,用base64,否则用file-loader产生真实的图片 use: { loader: 'url-loader', options: { limit: 1, // 200k 200 * 1024 outputPath: 'img/' // 打包后输出地址 在dist/img } } },
2.css
plugins: [ new MiniCssExtractPlugin({ filename: 'css/main.css' }), ]
3.希望输出的时候,给这些css\img
加上前缀,传到服务器也能访问
output: { filename: 'bundle.[hash:8].js', // hash: 8只显示8位 path: path.resolve(__dirname, 'dist'), publicPath: 'http://www.mayufo.cn' // 给静态资源统一加 },
4.打包多页应用
// 多入口 let path = require('path') let HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'development', entry: { home: './src/index.js', other: './src/other.js' }, output: { filename: "[name].js", path: path.resolve(__dirname, 'dist2') }, plugins: [ new HtmlWebpackPlugin({ template: './index.html', filename: 'home.html', chunks: ['home'] }), new HtmlWebpackPlugin({ template: './index.html', filename: 'other.html', chunks: ['other', 'home'] // other.html 里面有 other.js & home.js }), ] }
十二 配置source-map
module.exports = { devtool: 'source-map' // 增加映射文件调试源代码 }
1.源码映射 会标识错误的代码 打包后生成独立的文件 大而全 「source-map」
2.不会陈胜单独的文件 但是可以显示行和列 「eval-source-map」
3.不会产生列有行,产生单独的映射文件 「cheap-module-source-map」
4.不会产生文件 集成在打包后的文件中 不会产生列有行 「cheap-module-eval-source-map」
十三 watch
改完代表重新打包实体
module.exports = { watch: true, watchOptions: { poll: 1000, // 每秒监听1000次 aggregateTimeout: 300, // 防抖,当第一个文件更改,会在重新构建前增加延迟 ignored: /node_modules/ // 对于某些系统,监听大量文件系统会导致大量的 CPU 或内存占用。这个选项可以排除一些巨大的文件夹, }, }
十四 webpack
的其他三个小插件
每次打包之前删掉dist目录 yarn add clean-webpack-plugin -D
const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { output: { path: path.resolve(process.cwd(), 'dist'), }, plugins: [ new CleanWebpackPlugin('./dist') ] }
2.copyWebpackPlugin
一些静态资源也希望拷贝的dist中
yarn add copy-webpack-plugin -D
const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { plugins: [ new CopyWebpackPlugin([ {from: 'doc', to: './'} ]) ] }
3.bannerPlugin
内置模块
版权声明
const webpack = require('webpack'); new webpack.BannerPlugin('hello world') // or new webpack.BannerPlugin({ banner: 'hello world'})
十五 webpack解析resolve
以bootstrap
为例
npm install bootstrap -D
index.js
import 'bootstrap/dist/css/bootstrap.css'
报错
ERROR in ./node_modules/bootstrap/dist/css/bootstrap.css 7:0 Module parse failed: Unexpected token (7:0) You may need an appropriate loader to handle this file type. | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | */ > :root { | --blue: #007bff; | --indigo: #6610f2; @ ./src/index.js 22:0-42 @ multi (webpack)-dev-server/client?http://localhost:8081 ./src/index.js
这是因为bootstrap
4.0的css引入了新的特性,CSS Variables
安装 npm install postcss-custom-properties --save-dev
配置webpack.config.js
{ test: /\.css$/, use: ['style-loader', 'css-loader', { loader: 'postcss-loader', options: { plugins: (loader) => [ require("postcss-custom-properties") ] } }] }
但是每次引入都很长,如何优雅引入
resolve: { // 在当前目录查找 modules: [path.resolve('node_modules')], alias: { 'bootstrapCss': 'bootstrap/dist/css/bootstrap.css' } },
import 'bootstrapCss' // 在node_modules查找
省略扩展名
resolve: { // 在当前目录查找 modules: [path.resolve('node_modules')], // alias: { // 'bootstrapCss': 'bootstrap/dist/css/bootstrap.css' // }, mainFields: ['style', 'main'], // 先用bootstrap中在package中的style,没有在用main // mainFiles: [] // 入口文件的名字 默认index extensions: ['.js', '.css', '.json'] // 当没有拓展命的时候,先默认js、次之css、再次之json },
定义环境变量
DefinePlugin
允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和生产模式的构建允许不同的行为非常有用。
let url = '' if (DEV === 'dev') { // 开发环境 url = 'http://localhost:3000' } else { // 生成环境 url = 'http://www.mayufo.cn' }
webpack.config.js
new webpack.DefinePlugin({ // DEV: '"production"', DEV: JSON.stringify('production'), FLAG: 'true', // 布尔 EXPRESSION: '1 + 1' // 字符串 如果希望是字符串 JSON.stringify('1 + 1') })
区分两个不同的环境
分别配置不同的环境
webpack.base4.js
基础配置
webpack.dev4.js
开发环境
webpack.prod4.js
生产环境
yarn add webpack-merge -D
npm run build -- -- config webpack.dev4.js npm run build -- -- config webpack.build.js
webpack.base4.js
let path = require('path') let HtmlWebpackPlugin = require('html-webpack-plugin') let CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { entry: { home: './src/index.js' }, output: { filename: "[name].js", path: path.resolve(process.cwd(), 'dist3') }, module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [ '@babel/preset-env' ] } } }, { test: /\.css$/, use: ['style-loader', 'css-loader', { loader: 'postcss-loader', options: { plugins: (loader) => [ require("postcss-custom-properties") ] } }] } ] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', filename: 'index.html' }) ] }
webpack.dev4.js
let merge = require('webpack-merge') let base = require('./webpack.base4.js') module.exports = merge(base, { mode: 'development', devServer: {}, devtool: 'source-map' })
webpack.prod4.js
let merge = require('webpack-merge') let base = require('./webpack.base4.js') module.exports = merge(base, { mode: 'production' })
package.json
"scripts": { "build": "webpack --config webpack.prod4.js", "dev": "webpack-dev-server --config webpack.dev4.js" },
十六 多线程打包happypack
yarn add happypack
let Happypack = require('happypack') rules: [ { test: /\.js$/, exclude: '/node_modules/', include: path.resolve('src'), use: 'happypack/loader?id=js' }, ] plugins: [ new Happypack({ id: 'js', use: [{ loader: 'babel-loader', options: { presets: [ '@babel/preset-env', '@babel/preset-react' ] } }] }) ]
js启用多线程,由于启用多线程也会浪费时间,因此当项目比较大的时候启用效果更好
css启用多线程
{ test: /\.css$/, use: 'happypack/loader?id=css' } new Happypack({ id: 'css', use: ['style-loader', 'css-loader'] }),
十七 抽取公共代码
1.抽离自有模块
module.exports = { optimization: { splitChunks: { // 分割代码块,针对多入口 cacheGroups: { // 缓存组 common: { // 公共模块 minSize: 0, // 大于多少抽离 minChunks: 2, // 使用多少次以上抽离抽离 chunks: 'initial' // 从什么地方开始, 从入口开始 } } } }, }
2.抽离第三方模块
optimization: { splitChunks: { // 分割代码块,针对多入口 cacheGroups: { // 缓存组 common: { // 公共模块 minSize: 0, // 大于多少抽离 minChunks: 2, // 使用多少次以上抽离抽离 chunks: 'initial' // 从什么地方开始,刚开始 }, vendor: { priority: 1, // 增加权重, (先抽离第三方) test: /node_modules/, // 把此目录下的抽离 minSize: 0, // 大于多少抽离 minChunks: 2, // 使用多少次以上抽离抽离 chunks: 'initial' // 从什么地方开始,刚开始 } } }, },
十八 懒加载(延迟加载)
yarn add @babel/plugin-syntax-dynamic-import -D
source.js
export default 'mayufo'
index.js
let button = document.createElement('button') button.innerHTML = 'hello' button.addEventListener('click', function () { console.log('click') // es6草案中的语法,jsonp实现动态加载文件 import('./source.js').then(data => { console.log(data.default) }) }) document.body.appendChild(button)
{ test: /\.js$/, exclude: '/node_modules/', include: path.resolve('src'), use: [{ loader: 'babel-loader', options: { presets: [ '@babel/preset-env', '@babel/preset-react' ], plugins: [ '@babel/plugin-syntax-dynamic-import' ] } }] }
十九 热更新(当页面改变只更新改变的部分,不重新打包)
plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', filename: 'index.html' }), new webpack.NameModulesPlugin(), // 打印更新的模块路径 new webpack.HotModuleReplacementPlugin() // 热更新插件 ]
index.js
import str from './source' console.log(str); if (module.hot) { module.hot.accept('./source', () => { console.log('文件更新了'); require('./source') console.log(str); }) }