webpack
什么是Webpack?
webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler),当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle
image
使用Webpack作为前端构建工具:
-
代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。
-
文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
-
代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
-
模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
-
自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
-
代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
-
自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
在webpack
应用中有两个核心: - 模块转换器,用于把模块原内容按照需求转换成新内容,可以加载非 JS 模块
- 扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。
安装
-
初始化:cnpm init -y
-
安装依赖:cnpm i -D webpack webpack-cli html-webpack-plugin html-withimg-loader uglifyjs-webpack-plugin clean-webpack-plugin copy-webpack-plugin webpack-dev-server happypack expose-loader file-loader webpack-dev-middleware webpack-merge url-loader style-loader css-loader less-loader less scss-loader sass babel-loader @babel/core @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-syntax-dynamic-import @babel/plugin-transform-runtime @babel/preset-env @babel/preset-react mini-css-extract-plugin optimize-css-assets-webpack-plugin postcss-loader eslint eslint-loader autoprefixer
webpack可以0配置
-
打包工具->输出后的结果(js模块)
-
打包(支持我们的js的模块化)
手动配置
配置文件的名字 webpack.config.js
- (当配置文件的名字为其他文件名比如:webpack.config.my.js)运行时用npx webpack --config webpack.config.my.js
webpack优化
webpack自带优化,当mode时生产环境时,
-
使用import引入,会自动去除掉没用的代码,tree shaking。
-
在es模块时,比如用require('./xxx)会把结果放到default上。比如let calc=require('./test');calc.default.xxx();但是不支持tree shaking
-
scope hosting 作用域提升,//会自动省略,可以简化代码,例如,let a=1;let b=2;let c=3;let d=a+b+c;console.log(d)。打包后会简化代码。
webpack配置
let path = require('path');
let webpack = require('webpack');
let Happypack = require('happypack');
let HtmlWebpackPlugin = require('html-webpack-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let UglifyJs = require('uglifyjs-webpack-plugin');
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let { CleanWebpackPlugin } = require('clean-webpack-plugin');
let CopyWebpackPlugin = require('copy-webpack-plugin');
//模块happypack可以实现多线程打包进程
//三个小插件
//1.cleanWebpackPlugin //每次打包把原有的文件全部删除
//2.copyWebpackPlugin //copy插件把A文件移到B文件下
//3.bannerPlugin内置
module.exports = {
devServer: {//开发服务器的配置
port: 5312,//端口号
// progress: true,//打包进度
contentBase: './dist',//打包后生成的文件
open: true,//打包完浏览器打开
hot: true,//热更新开关
compress: true,//开启压缩
//1.代理服务
// proxy: {
// '/api': {
// target: 'http://localhost:3000',
// //重写的方式把请求代理到express服务器上
// pathRewrite: { '/api': '' },
// }//配置了一个代理
// },
//2.前端mock数据
// before(app) {//提供的钩子
// app.get('/user', (req, res) => {
// res.json({ name: 'dali-mock' })
// })
// },
//3.服务端,不用代理处理,能在服务端中启用webpack端口用服务端口,通过自己写的服务,来启动webpack
//---服务端代码
// let express = require('express');
// let app = express();
// let webpack = require('webpack');
// //中间件
// let middle = require('webpack-dev-middleware');
// let config = require('./webpack.config.js');
// let compiler = webpack(config);
// app.use(middle(compiler));
// app.get('/user', (req, res) => {
// res.json({ name: 'dali2-服务端' })
// });
// app.listen(3000);
//---服务端代码
},
resolve: {//解析第三方包 common
modules: [path.resolve('node_modules')],
extensions: ['.js', '.css', '.less', '.scss', '.jsx'],//扩展名,在组件中引入文件时,在没有后缀的情况下,会依次按顺序查找
// mainFields: ['style', 'main'],//优先查找style,找不到再查找main
// mainFields:[],//入口文件的名字 默认index.js
alias: {//别名
bootstrap: 'bootstrap/dist/css/bootstrap.css'
},
},
optimization: {//优化项配置--之前是commonChunkPlugins
minimizer: [
new UglifyJs({//压缩js
cache: true,//缓存
parallel: true,
sourceMap: true,//映射
}),
new OptimizeCss(),//压缩css
],
splitChunks: {//分割代码块-----优化,
cacheGroups: {//缓存组
common: {//公共的模块
chunks: 'initial',//
minSize: 0,//大小
minChunks: 3,//引用多少次
},
vendor: {
priority: 1,//权重,优先
test: /node_modules/,//把你抽离出来
chunks: 'initial',
minSize: 0,//大小
minChunks: 2,//引用多少次
}
}
}
},
mode: 'development',//默认有两种,production ,development
entry: './src/index.js',//入口
// entry: {//多页面多入口
// home: './src/home.js',
// other: './src/other.js',
// },
// entry: {
// // test: './src/reactTest.js',//入口文件名及对应的路径
// react: ['react', 'react-dom'],
// },
//-----调试
//1.源码映射会单独胜场一个sourcemap出错了会标识当前报错的列和行,大而全,独立的
//devtool: 'source-map',//增加映射文件可以帮我们调试源文件代码,大而全,独立的
//2.不会产生单独的文件但是可以显示行和列。
devtool: 'eval-source-map',//---调试作用
//3.不会产生列,但是是一个单独的映射文件
//devtool: 'cheap-module-source-map',//产生后你可以保留起来
//4.不会产生文件,集成在打包后的文件中,也不会产生列
// devtool: 'cheap-module-eval-source-map',
// ---watch,实时打包
// watch: true,
// watchOptions: { //监控属性
// poll: 1000,//每秒问我1000次
// aggregateTimeout: 500,//防抖我一直输入代码,输入代码之间间隔多长时间
// ignored: /node_modules/,//不需要进行监控哪个文件
// },
output: {//出口
filename: 'bundle.js',//打包后的文件名,加哈希戳每次打包生成新的 filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'dist'),//路径必须是一个绝对路径
// filename: '_dll_[name].js',
// path: path.resolve(__dirname, 'dist'),
// library: '_dll_[name]',//赋值给这个变量
// library: 'testResult',//赋值给这个变量
//libraryTarget: 'var',//commonjs,umd,this.var
// publicPath: 'http:www.tianchengli.cn',//可以在每个图片前加上这个地址
//----多页面的时候用[name].js 代表着不同页的名字
// filename: '[name].js',
// path: path.resolve(__dirname, 'dist'),//路径必须是一个绝对路径
//-----多页面
},
// externals: {
// jquery: '$'
// },
module: {
//模块
noParse: /jquery/,//不去解析jquery中的依赖关系,--优化
rules: [
{
test: /\.js$/,//nomal正常得顺序
// use: {
// loader: 'babel-loader',
// options: {//babel-loader需要把es6->es5
// presets: [
// '@babel/preset-env',
// '@babel/preset-react',
// ],
// plugins: [
// ['@babel/plugin-proposal-decorators', { 'legacy': true }],
// ['@babel/plugin-proposal-class-properties', { 'loose': true }],
// '@babel/plugin-transform-runtime'
// ]
// }
// },
use: 'Happypack/loader?id=js',
include: path.resolve(__dirname, 'src'),//只在哪里搜找,--优化
exclude: /node_modules/,//不在哪里搜找,--优化
},
{
test: /\.html$/,
use: 'html-withimg-loader'
},
{
test: /\.(png|jpg|gif)$/,
//做一个限制当我们的图片小于多少kb的时候,(limit:xxx),用base64转化
// use: 'url-loader',//base64转化
// use: 'file-loader',
use: {
loader: 'url-loader',
options: {
limit: 10 * 1024,
outputPath: '/img/',
// publicPath: 'http:www.tianchengli.cn',//在图片前加上链接地址
}
}
},
{
test: require.resolve('jquery'),
use: 'expose-loader?$'
},
// {//eslint,默认是从右往左执行,从下往上执行,所以加一个enforce
// test: /\.js$/,
// use: {
// loader: 'eslint-loader',
// options: {
// enforce: 'pre',//前面
// }
// }
// },
//规则css-loader解析@import这种语法的
//style-loader 是把css插入到head的标签中
//loader 希望单一,字符串只用一个loader,
//多个loader需要是一个[]
//loader默认从右向左执行,从下到上执行
//也可以把use写成一个数组,多传一个option
// { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
{ test: /\.css$/, use: 'Happypack/loader?id=css' },//用happypack优化
//postcss-loader加样式前缀
// { test: /\.css$/, use: [{ loader: 'style-loader', options: { insert: 'top' } }, 'css-loader'] },
// { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'] },
// { test: /\.less$/, use: [{ loader: 'style-loader', options: { insert: 'top' } }, 'css-loader', 'less-loader'] },
]
},
plugins: [
//数组,放着所有的webpack插件
new Happypack({
id: 'js',
use: [{
loader: 'babel-loader',
options: {//babel-loader需要把es6->es5
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
plugins: [
['@babel/plugin-proposal-decorators', { 'legacy': true }],
['@babel/plugin-proposal-class-properties', { 'loose': true }],
'@babel/plugin-transform-runtime',
'@babel/plugin-syntax-dynamic-import',
],
}
}]
}),
new Happypack({
id: 'css',
use: [{ loader: 'style-loader', }, 'css-loader']
}),
new webpack.DllReferencePlugin({//
manifest: path.resolve(__dirname, 'dist', 'manifest.json')
}),
// new webpack.DllPlugin({//name===library
// name: '_dll_[name]',
// path: path.resolve(__dirname, 'dist', 'manifest.json')
// }),
//---单页面的时候
new HtmlWebpackPlugin({
template: './src/index.html',//编译得模板
filename: 'index.html',//名字
// minify: {--优化
// removeAttributeQuotes: true,//去掉双引号
// collapseWhitespace: true,//压缩成一行
// },
hash: true,//支持哈希
}),
//---单页面的时候
//---多页面的时候
// new HtmlWebpackPlugin({
// template: './src/index.html',//编译得模板
// filename: 'home.html',//名字
// chunks: ['home']
// }),
// new HtmlWebpackPlugin({
// template: './src/index.html',//编译得模板
// filename: 'other.html',//名字
// chunks: ['other']
// }),
//---多页面的时候
// new MiniCssExtractPlugin({
// filename: 'css/main.css',//压缩css后引用得文件名
// }),
// new CleanWebpackPlugin(),//每次打包把原有的文件全部删除
// new CopyWebpackPlugin([{ from: 'doc', to: './' }]),//copy插件
// new webpack.BannerPlugin('make by -dali'),//在个人信息注释在代码前
// new webpack.ProvidePlugin({//在每个模块中都注入$
// $: 'jquery'
// }),
// new webpack.DefinePlugin({
// // DEV: "'dev'"
// DEV: JSON.stringify('dev'),
// FLAG: 'true',
// EXPORESSION: '1+1'
// }),
new webpack.IgnorePlugin(/\.\/locale/, /moment/),//优化moment包,
new webpack.NamedModulesPlugin(),//打印更新的模块路径
new webpack.HotModuleReplacementPlugin(),//热更新插件
],
}
package.json
{
"name": "webpack",
"version": "1.0.0",
"description": "",
"main": "webpack.config.js",
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-decorators": "^7.6.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/preset-env": "^7.6.2",
"@babel/preset-react": "^7.6.3",
"autoprefixer": "^9.6.1",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^5.0.4",
"css-loader": "^3.2.0",
"eslint": "^6.5.1",
"eslint-loader": "^3.0.2",
"expose-loader": "^0.7.5",
"file-loader": "^4.2.0",
"happypack": "^5.0.1",
"html-webpack-plugin": "^3.2.0",
"html-withimg-loader": "^0.1.16",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.8.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"style-loader": "^1.0.0",
"uglifyjs-webpack-plugin": "^2.2.0",
"url-loader": "^2.2.0",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9",
"webpack-dev-middleware": "^3.7.2",
"webpack-dev-server": "^3.8.2",
"webpack-merge": "^4.2.2"
},
"dependencies": {
"@babel/runtime": "^7.6.2"
}
}
webpack.dev.js
//npm run build -- --config webpack.dev.js
let { smart } = require('webpack-merge');
let base = require('./webpack.config.js');
module.exports = smart(base, {
mode: 'development',
})
webpack.prod.js
//npm run build -- --config webpack.prod.js
let { smart } = require('webpack-merge');
let base = require('./webpack.config.js');
module.exports = smart(base, {
mode: 'production',
})
webpack.react.js
let path = require('path');
let webpack = require('webpack');
module.exports = {
mode: 'development',
entry: {
// test: './src/reactTest.js',//入口文件名及对应的路径
react: ['react', 'react-dom'],
},
output: {
filename: '_dll_[name].js',
path: path.resolve(__dirname, 'dist'),
library: '_dll_[name]',//赋值给这个变量
// library: 'testResult',//赋值给这个变量
// libraryTarget: 'var',//commonjs,umd,this.var,jsonp,amd
},
plugins: [
// new webpack.DllReferencePlugin({
// manifest: path.resolve(__dirname, 'dist', 'manifest.json')
// }),
new webpack.DllPlugin({//name===library
name: '_dll_[name]',
path: path.resolve(__dirname, 'dist', 'manifest.json')
})
]
}
webpack文件结构.jpg