生产环境不同于开发环境在于,生产环境需要优化代码,保证在上线之后运行更快且兼容各大浏览器。以开发环境中css资源打包为例:先把css资源构建一个commmjs模块,形成一个字符串,接着在js文件中创建style标签将构建的字符串插入到style标签中,而如果样式资源很多,会导致js文件很大,同时由于浏览器加载Js文件的机制,势必影响性能,同时插入style标签内容可能也会导致闪屏的情况产生(由于css内容放在js文件中的,且html引入的也是js文件,当具体显示html文件的时候,会动态创建style标签,并将css内容插入到style标签中,同时将style标签插入到head标签中,最后页面显示css样式,而这个动态过程是在浏览器中完成,容易造成闪屏)。因此为了增加用户体验,提高代码运行速度以及兼容性,生产环境则需要解决上述问题,同时也包括:抽取出js文件中的css为单独文件,对代码进行压缩、处理兼容性问题等一系列问题,但是为什么不在开发环境就处理好呢?如果在开发环境中处理这些复杂的问题,会影响开发进度。
1.处理css文件
1.提取出css为单独文件
不同于将css文件写入到打包之后的js文件,此时的css抽取到了单独的css文件中,并通过link进行引入,使得闪屏问题进一步解决,同时js文件大小减小。
用法:
-
下载mini-css-extract-plugin
npm i -s mini-css-extract-plugin
-
引入
const miniCssExtractPlugin = require('mini-css-extract-plugin');
-
配置
// loader配置 module: { rules: [ // css配置 { test: /\.css$/, use: [ // 让loader取代style-loader,作用:提取js中的css成单独文件,也即类似于截获,当css文件整合到Js文件后, // 不再是通过style-loader将js文件中的代码插入到style标签,也是抽取出来形成单独的文件 miniCssExtractPlugin.loader, 'css-loader' ] } ] }, // plugins配置 plugins: [ new htmlWebpackPlugin({ template: './src/webpack_02.html' }), new miniCssExtractPlugin({ // 给css文件重命名 filename: 'css/main.css' }) ]
2.css兼容性处理
采取的是postcss,而postcss的实现需要两个插件:postcss-loader postcss-preset-env
下载:
npm i -s postcss-loader postcss-preset-env
postcss-preset-env作用:帮postcss找到package.json中browserlist里面的配置,通过配置加载指定的css兼容性样式
配置:
rules: [
// css配置
{
test: /\.css$/,
use: [
miniCssExtractPlugin.loader,
'css-loader',
// postcss-loader默认配置 'postcss-loader' 但是此处修改设置
// 修改配置
{
loader: 'postcss-loader',
options: {
postcssOptions: {
// 默认写法
ident: 'postcss',
// 方法一:这样写没有兼容性处理代码
// plugins: () => [
// require('postcss-preset-env')()
// ]
// 方法二:这样写有兼容性处理代码
// plugins: [
// require('postcss-preset-env')
// ]
// 方法三:或者将插件引入写在单独的配置js中
config: './postcss.config.js'
}
}
}
]
}
]
# 方法三的配置文件
# postcss.config.js
module.exports = {
plugins: [
require('postcss-preset-env')
]
}
package.json配置
// 可以去官网查看配置含义
"browserslist": {
// 开发环境 需要用此环境 需要设置node环境变量:process.env.NODE_ENV = development
"development": [
// 兼容最近一次版本的浏览器
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
// 生产环境 默认是看生产环境 和webpack.config.js中的mode没有关系
"production": [
// 99.8的浏览器兼容
">0.2%",
// 不用考虑已不用的浏览器和op_mini
"not dead",
"not op_mini all"
]
}
由于package.json默认是看生产环境,因此需要在webpack中设置node环境变量来进行查看开发环境:
process.env.NODE_ENV = 'development';
module.exports = {
...
}
原来的css文件:
#box2 {
width: 200px;
height: 200px;
background-color: #454545;
display: flex;
backface-visibility: hidden;
}
经过兼容性处理之后的css文件:
#box2 {
width: 200px;
height: 200px;
background-color: #454545;
display: -webkit-flex;
display: flex;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
3.压缩css
通过optimize-css-assets-webpack-plugin
插件完成
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// plugins配置
plugins: [
// 压缩css
new OptimizeCssAssetsWebpackPlugin()
],
4.完整css处理代码
/**
生产环境搭建
**/
const { resolve } = require('path')
const miniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// 配置开发环境
process.env.NODE_ENV = 'development';
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/index.js',
path: resolve(__dirname, 'bulid')
},
// loader配置
module: {
rules: [
// css配置
{
test: /\.css$/,
use: [
miniCssExtractPlugin.loader,
'css-loader',
// postcss-loader默认配置 'postcss-loader' 但是此处修改设置
// 修改配置
{
loader: 'postcss-loader',
options: {
postcssOptions: {
// 默认写法
ident: 'postcss',
// 这样写没有兼容性处理代码
// plugins: () => [
// require('postcss-preset-env')()
// ]
// 这样写有兼容性处理代码
// plugins: [
// require('postcss-preset-env')
// ]
// 或者将插件引入写在单独的配置js中
config: './postcss.config.js'
}
}
}
]
}
]
},
// plugins配置
plugins: [
new miniCssExtractPlugin({
// 给css文件重命名
filename: 'css/main.css'
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin()
],
// 模式
mode: 'development'
}
# postcss.config.js
module.exports = {
plugins: [
require('postcss-preset-env')
]
}
2.js处理
1.js语法检查eslint
为了统一规范代码书写,可以使用eslint来进行约束。
首先使用eslint,采用eslint-loader
来实现,但是eslint-loader
依赖于eslint
,因此两者都需要下载
npm i -s eslint-loader eslint
注意:eslint只检查js内容,且只需要检查自己写的源代码,第三方库不用检查,因此配置如下:
module: {
rules: [
{
// 只检查js文件内容
test: /\.js$/,
// 不用检查第三方库
exclude: /node_modules/,
loader: 'eslint-loader'
}
]
},
但是仅仅是以上配置,webpack打包时不知道如何去检查,则需要配置检查规则,一般使用:airbnb
,查看npmjs.com
发现eslint-config-airbnb
适合es6语法但是也适合react
,如果不需要使用react
,则可以使用eslint-config-airbnb-base
,其也适合es6+语法。查看其官网可以看到要下载三个包:eslint-plugin-import、eslint、eslint-config-airbnb-base
npm i -s eslint-plugin-import eslint eslint-config-airbnb-base
此外下载完成之后,由于此规则的使用是通过在package.json
中eslintConfig
中进行设置来进一步使用的,因此:
"eslintConfig": {
"extends": "airbnb-base"
}
测试js文件:
// 该文件格式故意写的不规范
function add (x,y) {
console.log(x+y);
}
add(2,3);
运行webpack
结果如下:可以看到有很多不规范的地方
E:\前端\nodejs\实践\js\webpack_01\src\index.js
1:1 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
1:1 error Too many blank lines at the beginning of file. Max of 0 allowed no-multiple-empty-lines
2:13 error Unexpected space before function parentheses space-before-function-paren
2:16 error A space is required after ',' comma-spacing
2:21 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
3:3 warning Unexpected console statement no-console
3:16 error Operator '+' must be spaced space-infix-ops
3:20 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
4:2 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
5:1 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
6:6 error A space is required after ',' comma-spacing
6:10 error Newline required at end of file but not found eol-last
✖ 12 problems (11 errors, 1 warning)
11 errors and 0 warnings potentially fixable with the `--fix` option.
但是一个一个的修改很麻烦,因此可以配置自动修复eslint错误
module: {
rules: [
{
// 只检查js文件内容
test: /\.js$/,
// 不用检查第三方库
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 自动修复eslint错误
fix: true
}
}
]
}
修复之后的index.js
function add(x, y) {
console.log(x + y);
}
add(2, 3);
但是出现以下的警告信息:
2:3 warning Unexpected console statement no-console
✖ 1 problem (0 errors, 1 warning)
原因:不期望出现console信息,但是开发阶段为了测试又不得不去console,但是为了不出现这些警告信息,可以设置eslint配置,即:在需要检查的js文件中会出现警告的一行上方写上:// eslint-disable-next-line
,即可不会出现警告信息了
function add(x, y) {
// 表示:下一行eslint所以规则都失效(下一行不进行eslint检查)
// eslint-disable-next-line
console.log(x + y);
}
add(2, 3);
2.js兼容性处理
在实际的打包后输出的代码是没有进行兼容性处理的,因此对于不支持es6语法的浏览器是不友好的。兼容性处理通过babel-loader
来实现,其依赖于@babel/core
npm i -s babel-loader @babel/core
兼容性处理有三种方式:
-
基本Js兼容性处理
问题:使用此方式只能转换基本语法,比如:promise不能转换
@babel/preset-env
npm i -s @babel/preset-env
配置:
module: { rules: [ /* 1. 基本兼容性处理 @babel/preset-env */ { // 对js文件进行兼容性处理 test: /\.js$/, // 不对第三方包进行兼容性处理 exclude: /node_modules/, loader: 'babel-loader', options: { // 预设:指示babel做怎么样的兼容性处理 presets: [["@babel/preset-env", { "targets": "defaults" }]] // 此处使用 { "targets": "defaults" }的原因:preset-env的行为与browserslist不同:当在Babel或browserslist配置中没有找到目标时,它不会使用defaults查询。如果你想使用defaults查询,你需要显式地将它作为目标传递: } } ] },
测试js
const fn1 = (x, y) => { return x + y; } console.log(fn1(1,2));
打包之后的Index.js
eval("var fn1 = function fn1(x, y) {\n return x + y;\n};\n\nconsole.log(fn1(1, 2));\n\n//# sourceURL=webpack:///./src/index.js?");
并且
-
全部js统一兼容性处理
工作机制:预先定义好所有可能的方法,当不兼容的时候,将该方法挂载到对应的对象上,比如Object中不兼容,直接挂载到Object对象上。
问题:当只需要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大
@babel/polyfill
npm i -s @babel/polyfill
用法:直接在需要兼容性的js文件头部进行引入即可:
import '@babel/polyfill'; const fn1 = (x, y) => { return x + y; } console.log(fn1(1,2));
结果:进行兼容性处理了,但是打包后的Index.js文件大小从原来的
1.25kb
转变为:437kb
-
部分兼容性处理
按需加载需要的兼容性处理代码
core-js
npm i -s core-js
不同于
@babel/polyfill
配置中使用targets:faluts
默认配置,此处需要对其进行按需配置。module: { rules: [ { // 对js文件进行兼容性处理 test: /\.js$/, // 不对第三方包进行兼容性处理 exclude: /node_modules/, loader: 'babel-loader', options: { // 预设:指示babel做怎么样的兼容性处理 presets: [["@babel/preset-env", { // 指定按需加载 useBuiltIns: 'usage', // 指定core-js版本 一般下载的时候可以看到版本 corejs: { version: 3 }, // 指定兼容性做到哪个版本浏览器 targets: { chrome: '60', firefox: '60', ie: '9', safari: '10', edge: '17' } }]] } } ] },
注意:使用的时候要把
import '@babel/polyfill';
给去掉打包结果:index.js大小:
1.29kb
3.js压缩处理
由于webpack在mode='production'
的时候,内置了插件处理压缩js,因此只需要将mode改为production
即可实现
3.html处理
由于html标签识别就显示出来,不识别就不显示出来。因此没有兼容性处理。但是可以对其进行压缩处理。
配置:
plugins: [
new htmlWebpackPlugin({
template: './src/webpack_02.html',
filename: 'index.html',
// 压缩html配置
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
],
生产环境总写:
/*
生产环境:优化代码
*/
const { resolve } = require('path');
const miniCssExtractPlugin = require('mini-css-extract-plugin');
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const htmlWebpackPlugin = require('html-webpack-plugin');
// 公共处理css
const commonCssPlugin = [
miniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
indent: 'postcss',
plugins: [
require('postcss-preset-env')
]
}
}
}
]
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/index.js',
path: resolve(__dirname,'build')
},
module: {
rules: [
{
// css配置
test: /\.css$/,
use: [...commonCssPlugin]
},
{
// less配置
test: /\.less$/,
use: [...commonCssPlugin,'less-loader']
},
/**
* 正常来讲,一个文件只能被一个loader处理,当一个文件要被多个loader处理,
* 那么一定要指定loader执行的先后顺序:
* 先执行eslint 再执行babel (原因:当通过语法之后再进行兼容性处理操作)
*/
{
// js配置 eslint
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
fix: true
}
},
{
// js 配置 babel
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {
version: 3
},
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
},
{
// 处理图片资源
test: /\.(jpg|PNG|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
esModule: false,
outputPath: 'imgs'
}
},
{
// 处理html中的图片资源
test: /\.html/,
loader: 'html-loader',
options: {
esModule: false
}
}
]
},
plugins: [
new miniCssExtractPlugin({
filename: 'css/index.css'
}),
new optimizeCssAssetsWebpackPlugin(),
// 处理html
new htmlWebpackPlugin({
template: './src/webpack_02.html',
filename: 'index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: 'production'
}