nodejs安装教程:https://blog.csdn.net/FED_AF/article/details/105747632)
:: 初始化npm
C:\Users\Administrator> npm init -y
package name: (administrator) webpack_test
::后面全部回车默认
::全局安装(webpack版本4.43.0,webpack-cli版本3.3.11)
C:\Users\Administrator> npm i webpack webpack-cli -g
::提供sw代码运行的服务器环境
C:\Users\Administrator> npm i serve -g
:: 初始化npm
项目根目录> npm init -y
package name: (webpack_test) 名称自己取,只要不和全局的一样就行
::后面全部回车默认
::安装webpack和webpack-cli
项目根目录> npm i webpack webpack-cli -D
::安装webpack-dev-server
项目根目录> npm i webpack-dev-server -D
::用来打包css
项目根目录> npm i css-loader style-loader -D
::用来打包sass(这里是需要装css-loader和style-loader的,但前面已经装过就不再装)
项目根目录> npm i sass sass-loader -D
::用来打包html
项目根目录> npm i html-webpack-plugin -D
::用来打包css引入的图片
项目根目录> npm i url-loader file-loader -D
::用来打包html的img元素引入的图片
项目根目录> npm i html-loader -D
::用来提取css成单独文件
项目根目录> npm i mini-css-extract-plugin -D
::用来处理css兼容性
项目根目录> npm i postcss-loader postcss-preset-env -D
::用来压缩css代码
项目根目录> npm i optimize-css-assets-webpack-plugin -D
::用来检查规范js代码(airbnb风格)
项目根目录> npm i eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import -D
::用来处理js代码的兼容性
项目根目录> npm i babel-loader @babel/preset-env @babel/core @babel/polyfill core-js -D
::用来实现PWA技术
项目根目录> npm i workbox-webpack-plugin serve -D
::用来多进程打包
项目根目录> npm i thread-loader -D
::用来配合dll使用,功能是将某个文件打包输出去,并在html中自动引入
项目根目录> npm i add-asset-html-webpack-plugin -D
::优化生产环境代码压缩
项目根目录> npm i terser-webpack-plugin -D
::jquery
项目根目录> npm i jquery -D
项目根目录
源代码目录src
构建后代码目录build
webpack.config.js
/*
使用dll技术,对某些库(第三方库:jquery、vue)进行单独打包
*/
const {resolve} = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
// 最终打包生成的[name]
// ['jquery']要打包的库是jquery
jquery: ['jquery']
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'dll'),
// 打包的库向外暴露出去的内容叫什么名字
library: '[name]_[hash]',
},
plugins: [
// 打包生成一个manifest.json,提供和jquery的映射关系
new webpack.DllPlugin({
// 映射库的暴露的内容名称
name: '[name]_[hash]',
// 输出文件路径
path: resolve(__dirname, 'dll/manifest.json')
})
],
mode: 'production'
};
// resolve用来拼接绝对路径的方法
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
/*
样式文件可以使用HMR功能,因为style-loader内部实现了
js文件默认不能使用HMR功能
解决:需要修改js代码,添加支持HMR功能的代码,只能处理非入口js文件的其他文件
html文件默认不能使用HMR功能(html不存在引用问题,没有优化的空间),同时会导致修改html文件后页面刷新无果
解决:修改entry入口,将html文件引入
*/
// 设置node环境变量
process.env.NODE_ENV = 'development';
module.exports = { // webpack配置
// 入口起点
entry: ['./src/index.js', './src/index.html'],
// 输出
output: {
// 输出文件名
filename: 'built.js',
// 输出路径
// __dirname是nodejs的变量,代表当前文件的目录绝对路径
path: resolve(__dirname, 'build')
},
// loader的配置
module: {
rules: [
// 详细loader配置
oneOf: [
{
// 匹配哪些文件
test: /\.css$/,
// 使用哪些loader进行处理,多个loader用use数组
use: [
// use数组中loader的执行顺序:从右到左,从下到上,依次执行
// 创建style标签,将js中的样式资源插入进去,添加到head中生效
'style-loader',
// 将css文件变成commonjs模块加载到js中,里面的内容是样式字符串
'css-loader'
]
},
{
test: /\.sass$/,
use: [
'style-loader',
'css-loader',
// 将sass文件编译成css文件
'sass-loader',
'sass'
]
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
// 使用一个loader,直接用loader
// 需要下载url-loader和file-loader
loader: 'url-loader',
options: {
// 当发现图片大小小于8kb,就会被base64处理,转化为字符串
// 优点:能够减少请求数量,减轻服务器压力
// 缺点:图片体积会更大,文件请求速度更慢
limit: 8 * 1024,
// 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
// 解析时会出现问题:[object Module]
// 解决:关闭url-loader的es6模块化,使用commonjs解析
esModule: false
}
},
{
// 处理图片资源
test: /\.html$/,
// 处理html文件的img图片(负责引入img,从而能被url-loader处理)
// 需要下载html-loader
loader: 'html-loader'
},
{
// 处理其他资源
exclude: /\.(css|js|html|jpg|png|gif|sass|json)$/,
loader: 'file-loader',
options: {
// 限制输出的文件名哈希值长度为10位,在加上本来的扩展名
name: '[hash:10].[ext]'
}
}
]
]
},
// plugins的配置
plugins: [
// 详细plugins的配置
// html-webpack-plugin
// 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
// 需求:需要有结构的HTML文件
new HtmlWebpackPlugin({
// 将 './src/icons/icon.ico'文件作为网页标签的图标引入
// favicon: './src/icons/icon.ico',
// 复制 './src/index.html'文件,并自动引入打包输出的所有资源
// 多个html需要多个HtmlWebpackPlugin,通过filename指定构建后的html路径和文件名
template: './src/index.html'
}),
new HtmlWebpackPlugin({
template: './src/inde.html',
filename: './build/c/inde.html'
}),
// 告诉webpack哪些库不参与打包,同时使用时的名称也得改
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
}),
// 将某个文件打包输出去,并在html中自动引入
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
],
// 模式
// mode: 'production'
mode: 'development',
// 开发服务器devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)
// 特点:只会在内存中编译打包,不会有任何输出
devServer: {
contentBase: resolve(__dirname, 'build'),
// 启动gzip压缩
compress: true,
// 自动打开浏览器
// open: true,
// 按如下配置即可在其他终端访问
host: '0.0.0.0',
// 开启热模块替换(HMR功能),省去不必要刷新
hot: true,
// 端口号
port: 3000
},
devtool: "eval-source-map"
};
/*
source-map是一种提供源代码到构建后代码映射的技术。用法如下:
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
source-map:外部
会提示错误代码准确信息和源代码的错误位置
inline-source-map:内联,将source-map嵌入到入口js中,构建速度更快
会提示错误代码准确信息和源代码的错误位置
hidden-source-map:外部,将source-map输出到一个map文件中
会提示错误代码错误原因但是没有错误位置,而且只能提示构建后代码的错误位置
eval-source-map:内联,每一个文件都生成对应的source-map
会提示错误代码准确信息和源代码的错误位置(只多了一个js文件的hash值)
nosources-source-map:外部
会提示错误代码准确信息,但是没有任何源代码信息
cheap-source-map:外部
会提示错误代码准确信息,但只能定位到行
cheap-module-source-map:外部
会提示错误代码准确信息,但只能定位到行
速度快:eval-cheap-source-map>eval-source-map>inline-source-map>cheap-source-map>...
调试友好:source-map>cheap-module-source-map>cheap-source-map>...
开发环境通常选择eval-source-map或eval-cheap-module-source-map
生产环境通常选择source-map或cheap-module-source-map
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
/*
缓存:
babel缓存:让第二次打包构建速度更快
文件资源缓存:让代码上线运行更好使用。
每次webpack构建时会生成一个唯一的hash值加入到文件名中,以此保证当文件修改时刷新缓存
使用contenthash保证只对更改过的文件重新生成hash值
*/
/*
tree shaking: 去除无用代码
前提: 1.必须使用ES6模块化 2.开启production环境
作用:减少代码体积
在packaage.json中配置"sideEffects": ["*.css"]表示除css文件外所有代码都进行tree shaking
*/
/*
code split: 代码分割
将较大的js文件分割成多个较小的js文件,从而实现并行加载,速度更快
*/
/*
PWA:渐进式网络开发应用程序(离线可访问)
通过workbox实现PWA
*/
// 复用loader
const commonCssLoader = [
// 提取js中的css成单独文件
MiniCssExtractPlugin.loader,
'css-loader',
// css兼容性处理:postcss --> postcss-loader postcss-preset-env
// 帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式
/* "browserslist": {
// 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
"development": [
"last 1 chrome version",//兼容谷歌浏览器最近的一个版本
"last 1 firefox version",
"last 1 safari version"
],
// 生产环境:默认是生产环境
"production": [
">0.2%",//考虑99.8%的浏览器的兼容性
"not dead",//不考虑不再使用的浏览器的兼容性
"not op_mini all"//不考虑op_mini浏览器的全部版本的兼容性
]
}*/
// 使用loader的默认配置
// 'postcss-loader',
// 修改loader的默认配置
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss的插件
require('postcss-preset-env')()
]
}
}
];
// 设置node环境变量
// process.env.NODE_ENV = 'development';
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.[contenthash:10].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
/*{
// 用eslint做js语法检查
// 只检查自己写的源代码,第三方的库是不用检查的
// 在package.json中的eslintConfig设置检查规则:
// 使用airbnb风格
test: /\.js$/,
// 排除node模块
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
// 自动规范源js的语法
fix: true
}
},*/
{
// 以下loader只会匹配一个,造成冲突的可以放到oneOf外面
oneOf: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.sass$/,
use: [...commonCssLoader, 'sass-loader', 'sass']
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPage: 'images',
esModule: false
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(css|js|html|jpg|png|gif|sass|json|ico)$/,
loader: 'file-loader',
options: {
outputPage: 'media',
name: '[hash:10].[ext]'
}
},
{
// 用babel做js兼容性处理
test: /\.js$/,
exclude: /node_modules/,
use: [
// 开启多进程打包。
// 进程启动时间大概为600ms,进程通信也有开销。
// 只有工作消耗时间比较长,才需要多进程打包
{
loader: 'thread-loader',
options: {
workers: 2 // 进程2个
}
},
{
loader: 'babel-loader',
options: {
// 预设:指示babel做怎么样的兼容性处理
// @babel/preset-env:基本兼容性处理:只能转换基本语法,不能转换promise等
// @babel/polyfill:全部兼容性处理(在入口js文件中引入即可:import '@babel/polyfill';):所有兼容性代码全部引入,代码体积太大
// core-js:按需处理兼容性
presets: [
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
corejs: {
// 指定core-js的版本
version: 3
},
// 指定兼容性做到哪个版本浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '8',
safari: '10',
edge: '17'
}
}
]
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
]
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
// favicon: './src/icons/icon.ico',
template: './src/index.html',
// 压缩html代码
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
}),
new MiniCssExtractPlugin({
// 对输出的文件重命名
filename: 'css/main.[contenthash:10].css'
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin(),
// 压缩css
new WorkboxWebpackPlugin.GenerateSW({
// 帮助serviceworker快速启动
// 删除旧的serviceworker
// 生成一个serviceworker配置文件
clientsClaim: true,
skipWaiting: true,
}),
// 告诉webpack哪些库不参与打包,同时使用时的名称也得改
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
}),
// 将某个文件打包输出去,并在html中自动引入
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
],
// 对于单页面应用,可以将node_module中代码单独打包一个chunk最终输出
// 对于多页面应用,可以自动分析多入口trunk中有没有公共的文件。如果有会打包成单独一个chunk
optimization: {
splitChunks: {
chunks: 'all'
}
},
mode: 'production',
// 下面这一行不是注释,是忽略eslint报的no-console警告
// eslint-disable-next-line
// mode: 'development',
devtool: "source-map",
externals: {
// 忽略库名 -- npm包名
jquery: 'jQuery'
}
};
{
"name": "webpack_code",
"version": "1.0.0",
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@babel/core": "^7.9.6",
"@babel/polyfill": "^7.8.7",
"@babel/preset-env": "^7.9.6",
"add-asset-html-webpack-plugin": "^3.1.3",
"babel-loader": "^8.1.0",
"core-js": "^3.6.5",
"css-loader": "^3.5.3",
"eslint": "^7.0.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.20.2",
"file-loader": "^6.0.0",
"html-loader": "^1.1.0",
"html-webpack-plugin": "^4.3.0",
"jquery": "^3.5.1",
"mini-css-extract-plugin": "^0.9.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"serve": "^11.3.0",
"style-loader": "^1.2.1",
"thread-loader": "^2.1.3",
"url-loader": "^4.1.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0",
"workbox-webpack-plugin": "^5.1.3"
},
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
},
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
},
"sideEffects": [
"*.css",
"*.sass"
]
}
/*
服务器代码
启动服务器指令:node server.js
*/
const express = require('express');
const app = express();
// 缓存保留时长
app.use(express.static('build', {maxAge: 1000 * 3600}));
// 监听3000端口
app.listen(3000);
...
import './print.js';
...
//热模块替换
if (module.hot) { // 用于开发环境
// 一旦module.hot为true,说明开启了HMR功能
/*
1、accept方法会监听print.js的变化,一旦发生变化,默认不会重新打包构建其他js,而是会执行后面的回调函数
2、每引入一个(自己写的)js,就要在if里面加一个用来监听的accept函数
*/
module.hot.accept('./print.js', function() {})
}
//代码分割、懒加载、预加载
用于生产环境,通过import动态导入语法能将某个文件单独打包成一个chunk
//通过/* webpackChunkName: 'test' */将输出的chunk设置为固定的名字
// 正常加载是并行加载(同时加载多个文件,文件越多加载速度越慢)
// 懒加载:当文件需要使用时才加载
// 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
// 懒加载或预加载都建立在代码分割的基础上
// 但是下面的语法eslint会报错,提示你import语句只能放在顶部,解决的方法,要么打包时关掉eslint,要么使用webpack.pro.js中的optimization实现按需加载
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
console.log(mul(4, 5));
});
/*
eslint不认识window、navigator全局变量
解决:需要修改package.json中eslintConfig配置
"env": {
"browser": true // 支持浏览器端全局变量
}
sw代码必须运行在服务器上
npm i serve -g
serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
sw只能用https访问,本地除外
*/
// 注册serviceWorker
// 处理兼容性问题
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js').then(() => {
console.log('注册成功');
}).catch(() => {
console.log('注册失败');
});
});
}
- HMR
- oneOf
- source-map
- oneOf
- babel缓存
- 多进程打包
- 文件资源缓存(hash-chunkhash-contenthash)
- tree shaking
- code split
- 懒加载/预加载
- pwa
- externals
- dll
进入项目根目录,执行webpack命令,默认会将webpack.config.js作为配置文件,开发环境中,可以将webpack.dev,js复制为webpack.config.js。生产环境同理,并不是不能直接指定或修改配置文件,只是这样来得快。对于某些第三方库,可以先使用webpack.dll.js打包,以后每次打包再用webpack.pro.js
devServer配置:见webpack.dev.js
进入项目根目录,执行npx webpack-dev-server命令
服务器配置:见server.js
进入项目根目录,执行node server.js命令(只是创建了服务器,并不能自动打包和刷新页面)
运行servicework的服务器启动命令:serve -s build