10-10-新webpack

webpack的简介

-D 表示项目上线不需要了 development 开发

webpack的好处

之前写的JQ版本的CRM项目:

  1. 项目中有大量的模块(JS文件,CSS文件,IMG文件…)
  2. 项目放到服务器,通过浏览器访问时,需要发送大量的请求,加载速度肯定慢
  3. 项目上线时,还需要手动合并压缩JS,CSS,HMTL
  4. 小图合大图,减少HTTP的请求次数,精灵图,base64…
  5. 一个文件依赖大量的外部资源 < script src=“jq.js”> < script src=“index.js” > < link href=“xx.css”>
    href=“xx.css”>
  6. JS高级语法通过bable转成JS低级语法,让浏览器识别,把LESS转成CSS

上面的问题可以通过自动化构建的工具解决:webpack

webpack是什么?

https://www.webpackjs.com/

  1. 基于node.js开发的一个工具。
  2. 是一个模块的打包工具。 webpack会把所有的文件都当作模块

模块规范: 如果定义模块,如何导出模块,如何导入模块。

  1. CmmonJS 模块规范 node
  2. ES6 模块规范 vue react
  3. CMD 模块规范
  4. AMD 模块规范
  5. UMD 模块规范

基于工程化项目开发,项目的入口只能有一个。 我们把依赖都放到这个入口文件中,如下:

import "./index.css"
import data from "./data.json"

发现浏览器根本不能识别,需要借助webpack这个工具,让浏览器识别。
在webpack4.0之后,支持零配置打包,你不需要进行任何配置。
在老的webpack版本,你需要写代码去配置你的webpack。

webpack使用

首先初始化项目:npm init -y
安装webpack:

    本地安装:npm i webpack webpack-cli
    全局安装:npm i webpack webpack-cli  -g

npx:

你要使用webpack命令,你必须全局安装webpack
如果你不想全局安装,那么就可以使用npx
npx 默认会临时全局安装webpack

开发模式打包 和 生产模式打包: 默认是生产模式打包

开发模式打包:
    npx webpack ./src/index.js  -o ./dist/main.js --mode=development
生产模式打包:
    npx webpack ./src/index.js  -o ./dist/main.js --mode=production

生产模式打包比开发模式打包多了一个代码压缩。

npx webpack ./src/index.js -o ./dist/main.js --mode=development是零配置打包。
零配置打包还是比较弱,一般情况下我们自己配置webpack。

配置webpack

项目的根目录下,创建一个webpack.config.js,webpack配置都写在这里面

配置入口和出口

// 这里写node代码,代码导出给webpack工具,
// 打包的时候回检测是否存在,存在就使用这些规范
const path = require("path")
module.exports={
    mode:"development",		//开发模式打包,代码不压缩
    // 默认使用生产模式production,代码压缩
    entry:"./src/index.js",	//入口
    output:{				//出口
        //filename:"main.js",
        //name表示原文件的名,后面跟的hash值
        filename:"[name].[chunkhash].js"  
        path:path.resolve(__dirname,"dist")
    }
}

配置OK后,只需要通过webpack命令就可以打包了,webpack会自动找webpack.config.js文件

配置处理css

style-loadercss-loader
让webpack处理样式: webpack默认不能处理css 我们配置让它可以处理css。
需要借助于Loader loader就是一个翻译官 可以把webpack不识别的代码翻译成webpack可以识别的代码。

处理css,需要两个loader,一个是style-loader css-loader:

  • css-loader:把css文件转成commonjs模块,加载到JS模块
  • style-loader: 创建一个style标签,把样式资源添加页面的头部中 所谓的内部样式

安装这两个loader:

    npm i style-loader css-loader -D		

-D 表示项目上线不需要了

入口文件中引入index.css

 import "./index.css"
module:{	//mdule 中配置各种loader
  rules:[	//规则,规则都放这里面
       {
           test:/\.css$/,
           use:[   //创建style标签用来放样式,style标签在head标签中
               "style-loader",
               "css-loader"    //把css文件转成commonjs模块
           ]
       }
   ]
}

loader的加载顺序:从下向上,从右向左,依次执行

配置处理less

less-loader
让webpack处理less文件:
肯定需要配置loader让webpack可以打包less文件。
这个loader叫:less-loader 这个less-loader还需要依赖less模块,所以如果没有安装less的话需要一起安装。

你需要安装两个模块:npm i less less-loader -D
less只需要安装,不需要配置

入口文件中引入index.less

import "./01.less"
{
    test:/\.less$/,	//匹配到.less结束的文件
    use:[
        "style-loader",	//	同css
        "css-loader",	//	同css
        "less-loader"	//	把less代码转化为css代码
    ]
}

生成配置新的html模板

html-webpack-plugin
自动创建HTML模板:

  1. 之前打包完后,生成一个main.js文件,需要手动放到index.html中
  2. 我们的想法:打包完后的main.js自动塞到index.html中。
  3. 现在我们在public下面的有一个index.html,这个index.html叫最基本的模块
  4. 我们的想法是,根据上面的模板自动创建出一个新的html文件,并把上main.js放到新的html文件中

安装:

npm i html-webpack-plugin -D
const HtmlWebpackPlugin =require("html-webpack-plugin")

// 配置plugin插件  配置插件需要new一下
plugins:[  //配置根据模板生成新的html文件
    new HtmlWebpackPlugin({ //并在这个html文件中引入打包好的js文件
        template:"./public/index.html"  //指定模板
        filename:"index.html"    //指定打包生成的HTML文件名
        //不写filename默认使用原文件名index.html,最好用index
    })
]

配置删除无用的文件

clean-webpack-plugin
当配置出口文件名时通常都不是写死的,常常使用hash值得形式,( filename:"[name].[chunkhash].js" )因此就会产生很多的hash值不同的出口文件,旧文件没有用但依旧在,这个插件就是为了删除旧的无用文件。

使用 clean-webpack-plugin 这个插件,可以清理dist目录下面没有用的文件,安装:

npm i clean-webpack-plugin -D
//这个是一个对象,需要解构
const { CleanWebpackPlugin } = require("clean-webpack-plugin")

    plugins:[  
    //打包之前会,删除整个目标dist文件下的所有旧的文件
        new CleanWebpackPlugin(),
    ]

配置打包css图片

配置webpack打包图片:
需要两个loader: file-loader url-loader
安装:

npm i file-loader url-loader -D

url-loader:比file-loader更加强大 url-loader依赖于file-loader 配置时,只会配置url-loader
file-loader能处理的url-loader也能处理

{	//处理css背景图片,背景图标等
	test:/\.(jpg|png|gif)$/
}	//会将打包的图片放到js中
{
    loader: "url-loader",
    options:{
        outputPath:"images",  //打包到那个文件
        // 图片小于10kb,处理成base64,大于就打包成文件
        limit:10*1024,
        //name:"[name].[ext]" //使用原文件名
        name:"[hash:24].[ext]"  //多使用hash值
        // 重命名,ext文件后缀,使用原文件后缀
    }
}

配置html中的图片

file-loader

img类型图片

如果我们的Html文件中,使用img标签形式的图片,webpack能处理吗?
答:不能的,因为打包生成的html文件中的img标签不变,需要借助一个loader将路径变成打包好的图片路径或者base64。

如果你想处理模板中的img类型的图片,还需要一个loader,叫html-loader。
安装:

npm i html-loader -D
//处理HTML中的img标签
{
    test: /\.html$/,
    loader:"html-loader"
}

配置字体图标

file-loader
配置webpack打包字体图标需要的loader也是 file-loader

入口文件中引入引入字体图标样式:

import "./fonts/iconfont.css"
//处理字体图标
{	//排除的意思
	exclude:/\.(css|js|html|json|less|jpg|png|gif)$/,
	loader:"file-loader"
    //options:{	配置存放目录
    //   name:'[hash:12].[ext]',
    //   outputPath:"source"
   //}
}

因为字体图标引入的是css,所以需要配置上面的css-loader。

配置开发服务器

webpack-dev-server
之前的打包是在硬盘上打包。之前的打包是直接生成到硬盘上的,明天打开看还会有。 使用代码npx webapck

作用是将打包好的那一堆文件托管的这个开发服务器上,后面通过该开发服务器访问打包好的代码文件。该服务器基于express。
配置开发服务器,需要安装一个模块:webpack-dev-server

npm i webpack-dev-server -D
devServer:{
    //配置开发服务器托管的资源文件夹路由,表示该服务器托管dist下面的资源
    contentBase:path.resolve(__dirname,"dist"),
    port:8080,  //开发服务器的端口
    open:true,  //自动打开浏览器
    compress:true,  //启动gzip压缩
}

只需要安装,不需要引入,只需要配置,但是打包的命令就变了
以前在硬盘中打包:npx webapck
现在在内存中打包:npx webpack-dev-server
在内存中打包看不见。
之前在硬盘上打包直接结束,在内存中打包相当于开了一个服务器,所以会一直闪。
在package.json中配置

  "scripts": {
    "serve":"npx webpack-dev-server",
    "build":"npx webpack",
  }

当终止操作时就会关闭服务器,就不能访问了。需要再次打开服务器输入代码:npm run serve

npm run serve 在内存中打包
npm run build 在硬盘中打包

抽离css

mini-css-extract-plugin
最好将图片,字体图标打包都放到一个文件夹里

默认情况下:打包的CSS是HTML的内部样式,我们的想放到外部,
专业的叫法:抽离CSS。
模块:mini-css-extract-plugin

npm i mini-css-extract-plugin -d
let MiniCssExtractPlugin = require("mini-css-extract-plugin")

//需要把前面的style-loader替换为 MiniCssExtractPlugin.loader
new MiniCssExtractPlugin({
    filename:"css/index.css" //打包到css文件夹下面的index.css中
})

压缩css、js、html

压缩css

optimize-css-assets-webpack-plugin
把抽离出来的CSS也进行压缩,有一个模块可以专门对CSS进行压缩,
安装:

npm install --save-dev optimize-css-assets-webpack-plugin
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

new OptimizeCssAssetsPlugin() //压缩css代码

压缩html

想在开发模式下,压缩HTML代码:

//配置根据模板生成新的html文件并在这个html文件中引入打包好的js文件
new HtmlWebpackPlugin({ 
    template: "./public/index.html", //指定模板
   // filename: "index.html"    //指定打包生成的HTML文件名
    //不写filename默认使用原文件名index.html,最好用index
    minify:{	//压缩html
        removeComments:true, //移除html中的注释
        collapseWhitespace:true,   //删除空白符和换行符
    }
}),

压缩JS:

uglifyjs-webpack-plugin
如果模式是生产模式 mode是production,默认就会压缩JS代码。
安装插件:

npm i uglifyjs-webpack-plugin -D
let uglifyjs = require('uglifyjs-webpack-plugin');

new uglifyjs({  //压缩js
    uglifyOptions:{
        output: {
            comments:false //去除注释
        }
    }
})

完整代码

// 这里写node代码,代码导出给webpack工具,
// 打包的时候回检测是否存在,存在就使用这些规范
const path = require("path")
// 根据模板生成新的html文件,并把打包好的js放到这个文件中
const HtmlWebpackPlugin = require("html-webpack-plugin")
//这个是一个对象,需要解构,打包前清空目标dist文件夹
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
//抽离css
let MiniCssExtractPlugin = require("mini-css-extract-plugin")
// 压缩抽离的css代码
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// 压缩JS代码
let uglifyjs = require('uglifyjs-webpack-plugin');
module.exports = {
    mode: "development",     //开发模式打包,代码不压缩
    // 默认使用生产模式production,代码压缩
    entry: "./src/index.js", //入口
    output: {                //出口
        //filename:"main.js",
        //name表示原文件的名,后面跟的hash值
        filename: "[name].[chunkhash].js",
        path: path.resolve(__dirname, "dist")
    },
    //module 中配置各种loader
    module: {
        rules: [ //规则,规则都放这里面
            {
                test: /\.css$/,  //匹配到.css结束的文件
                use: [   //创建style标签用来放样式,style标签在head标签中
                    //"style-loader", 
                    //如果要抽离css,就不能使用了,因为这个是把css处理成内部样式
                    MiniCssExtractPlugin.loader,
                    "css-loader"    //把css文件转成commonjs模块
                ]
            },
            {
                test: /\.less$/, //匹配到.less结束的文件
                use: [
                    "style-loader",	//	同css
                    "css-loader",	//	同css
                    "less-loader"	//	把less代码转化为css代码
                ]
            },
            // 处理css中的图片
            {
                test: /\.(jpg|png|gif)$/,    //处理css背景图片,背景图标等
                use: [
                    {
                        loader: "url-loader",
                        options: {
                            outputPath: "images",  //打包到那个文件
                            // 图片小于10kb,处理成base64,大于就打包成文件
                            limit: 10 * 1024,
                            //name:"[name].[ext]" //使用原文件名
                            name: "[hash:24].[ext]"  //多使用hash值
                            // 重命名,ext文件后缀,使用原文件后缀
                        }
                    }
                ]
            },
            //处理HTML中的img标签
            {
                test: /\.html$/,
                loader: "html-loader"
            },
            //处理字体图标
            {
                exclude: /\.(css|js|html|json|less|jpg|png|gif)$/,
                loader: "file-loader",
                options:{   //配置存放目录
                    name:'[hash:12].[ext]',
                    outputPath:"source"
                }
            }
        ]
    },
    // 配置plugin插件  配置插件需要new一下
    plugins: [
        //配置根据模板生成新的html文件并在这个html文件中引入打包好的js文件
        new HtmlWebpackPlugin({ 
            template: "./public/index.html", //指定模板
           // filename: "index.html"    //指定打包生成的HTML文件名
            //不写filename默认使用原文件名index.html,最好用index
            minify:{    //压缩html
                removeComments:true, //移除html中的注释
                collapseWhitespace:true,   //删除空白符和换行符
            }
        }),
        //打包之前会,删除整个目标dist文件下的所有旧的文件
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            filename:"css/index.css" //打包到css文件夹下面的index.css中
        }),
        new OptimizeCssAssetsPlugin(), //压缩css代码
        new uglifyjs({  //压缩js
            uglifyOptions:{
                output: {
                    comments:false //去除注释
                }
            }
        })
    ],
    devServer: {
        //配置开发服务器托管的资源文件夹路由,表示该服务器托管dist下面的资源
        contentBase: path.resolve(__dirname, "dist"),
        port: 8080,  //开发服务器的端口
        open: true,  //自动打开浏览器
        compress: true,  //启动gzip压缩

    }
}

复习

webpack:
1)基于Node开发的 webpack配置中的写法,是Node的写法
2)webpack打包非常多类型的模块 js
3)解决模块之是的依赖关系 减少模块之间的依赖
4)图

webpack安装:
安装Ndoe 构建过程需要借助node环境。
npm i webpack webpack-cli -g 全局安装
只要全局安装了,就可以使用webpack命令进行打包了
npm i webpack webpack-cli -d 局部安装
要打包,需要使用npx webapck 进行打包 推荐

测试是否安装成功:
    webpack -v
    npx webpack -v

webpack4.0:
支持零配置打包 功能比较弱 默认只能打包js和josn文件

配置webpack:
在项目的根目录下面,创建一个webpack.config.js

entry: 所有模块的入口,webapck打包时,先找入口。
output: 配置打好的资源放到什么地方,配置打包好文件的名字
module: 配置很多的loader 可以让webpack打包一些非JS类型的文件
plugins: 配置webpack的插件  可以让webpack执行更折任务,如打包优化,压缩等
devServer: 配置开发服务器   打包好后,直接放到开发服务器上, 配置跨域...
mode: 打包的模式   development 开发    production 生产

其他配置

处理css兼容

最新的CSS属性,有些浏览器是不能识别的,需要加上所谓的前缀,特别是CSS3中的一些属性
如何知道,一个CSS属性,是否有兼容性问题?
答:可以通过一个网站来查询 叫:can i ues 地址:https://caniuse.com/

配置webpack兼容CSS:
安装:

npm postcss-loader  postcss-preset-env -D

postcss-loader 是loader 针对postcss-loader还有很多插件 postcss-loader 处理兼容问题
postcss-preset-env 是postcss-loader的插件 处理不同的浏览器的

{
   test: /\.css$/,  //匹配到.css结束的文件
   use: [   //创建style标签用来放样式,style标签在head标签中
       //"style-loader", 
       //如果要抽离css,就不能使用了,因为这个是把css处理成内部样式
       MiniCssExtractPlugin.loader,
       "css-loader",    //把css文件转成commonjs模块
       {// 配置postcss-loader
           loader:"postcss-loader",
           options:{
               // ident:'postcss',
               plugins:(loader)=>[
                   require('postcss-preset-env')()
               ]
           }
       }
   ]
},

还需要配置兼容哪些浏览器 在 package.json 中 配置

"browserslist": {
   "development": [
     "last 1 chrome version",
     "last 1 firefox version",
     "last 1 safari version"
   ],
   "production":[
     "> 0.2%",
     "not dead"
   ]
 }

ESLint 校验js代码规则

ESLint: 有些公司是强制使用ESLint的 刚开始使用时,非常不舒服。
ESLint 是一个插件化并且可配置的 JavaScript 语法规则和代码风格的检查工具。
作用:校验我们写的JS代码是否符合规则(可以自己定制,也可以使用第三方)

如果不满足规则,就报错,打包也打包不成功。
安装:

npm i eslint eslint-loader eslint-plugin-import eslint-config-airbnb-base -D

eslint-loader是用来校验的 依赖于eslint

针对的模块是JS模块。 只能校验你写的JS代码,不要去校验别人写的JS代码。

步骤:
1)配置校验JS的loader,在 module:{ rules: [

{
    test:/\.js$/,
    exclude:/node_modules/,
    loader:"eslint-loader",
    // 配置自动修复
    options:{
        fix:true
    }
}

2)告诉webpack校验规则在哪里 在package.json中进行配置

 "eslintConfig": {
    "extends": "airbnb-base"
  }

3)写JS代码:

function sum(num1,num2) { return num1+num2 }
console.log(sum(10,20)); console.log("hello eslint")

代码是没有任何问题,只是写法,不符合arabnb规范,这个规范是第三方的规范。
规则中不允许有console.log(), 如果你的代码有一行,需要eslint忽略校验,需要使用一个注释:

// eslint-disable-next-line
console.log(111)

处理JS代码的兼容性

我们写的JS代码,在比较低版本的浏览器中并不能识别,需要做兼容性处理。
使用ES6、ES7,ES8新版本的标准的写法,在有的低版本浏览器中并不能识别。
目的:ES6/7/8/9 ----> 编译老的写法 JS的兼容性处理

这个转化的写法,叫polyfill 把新的写法,使用JS模拟出来。
如箭头函数的polyfill 就是把箭头函数转成传统的function写法

配置webpack处理JS代码的兼容性。
高级JS语法:
1)可以转成低级写法 如:箭头函数 转成 function 解决:babel-loader
2)根本没有办法转 polyfill去模拟高级写法 解决:@babel/polyfill 有人把

安装:

npm i babel-loader @babel/core @babel/preset-env -D

babel-loader依赖于 @babel/core @babel/preset-env

配置:
使用babel-loader,但是还需要使用预设:

rules:[
   {	 //处理js的兼容性
       test:/\.js$/,
       exclude:/node_modules/,	//排除node_modules的文件
       loader:"babel-loader",
       options:{
           presets:[  // 预设
               "@babel/preset-env"
           ]
       }
   }
]

使用babel-loader 并不能把所有的ES新的写法。转成传统写法。此时就可以使用 polyfill。如promise,是一种新的写法,并不存在老的写法,所以没法转,只能模拟效果。

安装:

npm i @babel/polyfill -D

@babel-polyfill包中模拟非常非常多的JS高级写法
使用 @babel/polyfill 并不是说模拟出来的JS支持所有的浏览器。

使用:入口中引入,index.js 全局引入polyfill

import "@babel/polyfill"  

非常大缺点:体积非常大。小项目不推荐全局引入

如果要按需引入,需要借助一个模块,叫core-js,配置这个模块可以达到按需引入的目的。
安装:

npm i core-js -D   
{   //处理js的兼容性
     test:/\.js$/,
     exclude:/node_modules/,
     loader:"babel-loader",
     options:{
         presets:[  // 预设
             "@babel/preset-env",
             // 配置按需引入
             {
                 useBuiltIns:'usage',
                 corejs:{    //core-js的版本
                     version:3
                 },
                 // 指定兼容做到哪个版本的浏览器
                 targets:{
                     chrome:'60',
                     firefox:'60',
                     ie:'9',
                     safari:'10',
                     edge:'17'
                 }
             }
         ]
     }
 },

loader的优先执行

对于webpack来说,一切都是模块。
在webpack.config.js中针对JS模块,我们使用eslint-loader,还使用了babel-loader,当然也使用了ployfill。现在面临这样的问题:

  • eslint是用来做语法检查,如果有语法错误,就没有必要做JS兼容了。
  • bable把ES6/7/8 ----> ES5 ES6/7/8的代码通过ESlint检验 转成ES5时,可能就没有通过校验。

目标:先经过eslint-loader进行处理,处理OK后,再给bable-loader来处理。

 {
    test:/\.js$/,
    exclude:/node_modules/,
    loader:"eslint-loader",
    enforce:"pre",  // 优先执行eslint-loader 通过后再执行其它的loader
    options: {
        fix:true
    }
},

enforce:“pre”, 优先执行eslint-loader 通过后再执行其它的loader

较完整的生产环境的配置代码

// 一个比较完整版本的用于生产环境的webpack配置
let { resolve } = require("path")
let HtmlWebpackPlugin = require("html-webpack-plugin")
let { CleanWebpackPlugin } = require("clean-webpack-plugin")
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
let MiniCssExtractPlugin = require("mini-css-extract-plugin")

// 处理样式的公共代码
let commonCssLoader = [
    // MiniCssExtractPlugin.loader,
    {
        loader: MiniCssExtractPlugin.loader,
        options: {
            publicPath:"../"
        }
    },
    "css-loader",
    {
        loader:"postcss-loader",
        options:{
            ident:"postcss",
            plugins: () => [
                require("postcss-preset-env")()
            ]
        }
    }
]
module.exports = {
    mode:"production",
    entry:"./src/index.js",
    output:{
        filename:"js/main.js",
        path:resolve(__dirname,"dist")
    },
    module:{
        rules:[
            // 1,样式相关的  css  less  scss  stylus
            {
                test:/\.css$/,
                use:[...commonCssLoader]
            },
            {
                test:/\.less$/,
                use:[...commonCssLoader,"less-loader"]
            },
            // 2,JS相关的
            {
                test:/\.js$/,
                exclude:/node_modules/,
                loader:"eslint-loader",
                enforce:"pre",
                options:{
                    fix:true
                }
            },
            {
                test:/\.js$/,
                exclude: /node_modules/,
                loader:"babel-loader",
                options: {
                    presets:[
                        [
                            '@babel/preset-env',
                            // 配置polyfill的按需加载
                            {
                                // 配置按需引入
                                useBuiltIns:'usage',
                                corejs:{
                                    version:3
                                },
                                // 指定兼容做到哪个版本的浏览器
                                targets:{
                                    chrome:'60',
                                    firefox:'60',
                                    ie:'9',
                                    safari:'10',
                                    edge:'17'
                                }
                            }
                        ]
                    ]
                }
            },
            // 3,图片相关的
            {   // 配置是css中的背景图
                test:/\.(jpg|png|gif)$/,
                loader:"url-loader",
                options: {
                    limit:5 * 1024, // 图片小于5kb  会打包成base64
                    name:'[hash:12].[ext]',
                    outputPath:"img"
                }
            },
            {
                // 配置html模块中的插入图
                test:/\.html$/,
                loader:"html-loader",
            },
            // 4,字体图标相关的
            {
                exclude: /\.(js|json|css|less|jpg|png|gif|html)$/,
                loader: "file-loader",
                options: {
                    name:'[hash:12].[ext]',
                    outputPath:"source"
                }
            }
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:"./public/index.html",
            // 配置生成的html文件
            minify:{
                collapseWhitespace:true,
                removeComments:true
                // ....
            }
        }),
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            filename:"css/index.css"
        }),
        new OptimizeCssAssetsPlugin()
    ]
}

HML模块热替换

现在使用webpack进行打包时,有这样一问题:
项目中有很多的模块,使用webpack-dev-serve进行打包时,由于它是在内存中进行打包的,当我们修改了一个模块的代码,它会实时打包,我们明明改了css模块,但是js模块也会重新打包。这样,如果项目非常大,我们改一个小模块,其它模块都会重新进行打包,这样,不太完美。

解决:使用webpack中提供的热模块替换 只针对开发模式 只能使用内存形式打包

在webpack中,下面的几类的模块支持热模块替换:
1)样式模块:支持HMR,必须使用style-loader 非常多

 devServer:{
 contentBase:resolve(__dirname,"dist"),
    port:8080,
    open:true,
    compress:true,
    hot:true, // 支持热模块替换
 }

2)HTML模块 由于HTML模块基本上我们不会动 所以基本上不会对HTML文件进行HMR
如果确实要对HTML文件进行HMR,需要配置多入口:

entry:["./src/index.js","./public/index.html"],
devServer:{
    contentBase:resolve(__dirname,"dist"),
    port:8080,
    open:true,
    compress:true,
    hot:true, // 支持热模块替换
}

3)JS模块 默认是不支持HMR功能
如果想让它支持HMR功能,需要在入口JS中配置,index.js
JS模块HMR,不能针对入口文件(针对是非入口)

if (module.hot) {
    // 使用HMR功能
    // 针对vuex.js模块进行HMR
    module.hot.accept("./js/vuex.js",()=>{
        logVuex(); //函数
    })
}

Source-Map

在JS模块中,写的JS代码,可能会出错误。由于你的JS模块被打包了,那么你,很难定位到你的错误。

打包后的JS模块,如果你想调试,那么就可以使用sourcemap。
在webpack.config.js中配置

// 开启source-map调试
devtool:"source-map"

source-map配置选项非常多:

[inline- | hidden- | eval- | nosources- | cheap- | cheap-module ]source-map

source-map: 外部映射

  1. 生成源代码到构建后代码的映射代码 此映射文件—>帮你精确定位错误
  2. 精确 哪个文件 此文件的哪个位置

inline-source-map: 内部映射

  1. 并不会生成所谓的映射文件
  2. 帮我们定义到哪个文件的哪个位置出错了

hidden-source-map:外部映射

  1. 不能帮我们定位到哪个文件的哪个位置出错了
  2. 生成所谓的映射文件

eval-source-map: 内部映射

  1. 并不会生成所谓的映射文件
  2. 帮我们定位到哪个文件的哪个位置出错了

nosources-source-map:

  1. 生成所谓的映射文件
  2. 能找到哪个文件出错了,但是找不到是此文件的哪个位置出错了

cheap-source-map:外部映射
cheap-module-source-map:外部映射

  1. 生成所谓的映射文件
  2. 帮我们定位到哪个文件的哪个位置出错了

速度排名:eval > inline > cheap > …

开发环境:调试友好,构建速度快一点 通常开发时,都是在内存中打包
eval-source-map : 速度快 定位到错误
eval-cheap-source-map 速度快 定位到错误 生map文件

生产环境:隐藏源代码(不要定位错误) 代码体积小(不要有map文件)
hidden-source-map
nosources-source-map

 // 开启source-map调试
 // devtool:"source-map"
 // devtool:"inline-source-map"
 // devtool:"hidden-source-map"
 // devtool:"eval-source-map"
 // devtool:"nosources-source-map"
 devtool:"cheap-module-source-map"

补充配置

OneOf

在webpack.config.js中配置很多的loader,这些loader是用来处理不同的模块的,一个模块,默认情况下,都要被所有的loader过滤一下。

默认情况下,所有的模块进入rules中会都走一遍,没有必要,可以使用OneOf,只会匹配到一个loader,提升打包速度。

rules:[
	{
		 test:/\.js$/,
		 ......
	}
	{
		oneOf:[
			{test:/\.css$/,......},
			{test:/\.js$/,.....}
		]
	}
]

设置缓存

构建出来的项目,需要放到服务器上。服务上面放的是我们打包好的项目。

要设置缓存,需要后端支持server.js。后端设置如下:

 app.use(express.static(path.resolve(__dirname,"../dist"),{maxAge:3600*1000}))

如果后端设置了缓存,即使你重新打包,后端服务器也托管dist目录,效果也不会发生改变。webpack也需要设置

开启缓存:cacheDirectory:true 针对babel来设置的。

{
test:/\.js$/,
exclude: /node_modules/,
loader:"babel-loader",
options: {
	presets:[
    ],
    // 开启babel缓存
    cacheDirectory:true
    }
}

使用缓存步骤:

  1. 后端设置 {maxAge:3600*1000}
    不管你动没动你的代码 都的一直是缓存。因为文件名没有发生改变
  2. webpack 需要设置 cacheDirectory:true
  3. 设置打包后的文件名 只有文件名发生改变,使用hash值
     output:{
       filename:"js/main.[hash:10].js",
          path:resolve(__dirname,"dist")
      },
      new MiniCssExtractPlugin({
          filename:"css/index.[hash:10].css"
      }),
    

这样,没有缓存了。 不管代码动没动,每一次打包,都会生成一个新的文件名,文件名变了,重新请求服务器,不会再走缓存了。没有达到目的。

目标:如果代码没有动,走缓存,如果代码动了,不走缓存。

上面的设置就不OK了,也就是说,hash值,需要根据文件内容来计算出来hash,如果内容没有变,hash值是不变,只有内容变了,hash值就变了。这样就能达到目的。

如何根据内容计算出hash值?
答:contenthash
内容变了,hash值才会变,文件名会变,不走缓存
内容不变,hash值不会变,文件名不变,要走缓存

output:{
        filename:"js/main.[contenthash:10].js",
        path:resolve(__dirname,"dist")
     },
      new MiniCssExtractPlugin({
         filename:"css/index.[contenthash:10].css"
     }),

Tree Shaking 树摇

去除掉无用的代码,减少代码体积。 tree shaking 是一种代码优化技术。
如:项目中引入elementui 默认是引入了所有的组件,默认webpack支持树摇,项目中没有使用到的组件,webpack不会进行打包。

默认开启tree shaking的前提:

  1. 使用的模块化是ES6模块化
  2. 基于开发环境
  3. webpack版本大于等于4

Tree Shaking用于去掉无用的代码:
有些第三方模块,不想进行Tree Shaking,需要关闭Tree Shaking,如何关闭:

// package.json文件
{
    "sideEffects": false
}

直接关闭Tree Shaking 不可取。

可以这样配置: “sideEffects” 后面跟一个数组,数据里面的选项表示关闭Tree Shaking

"sideEffects": [	//关闭下面文件的树摇
    "dist/*",
    "es/**/style/*",
    "lib/**/style/*"
  ]

除了可以在package.json中配置之外,还可以在rules中进行配置
参考:https://zhuanlan.zhihu.com/p/41795312

代码分割

多入口,不常用

多入口:如果项目比较大,打包出来的main.js也是非常大,加载就非常多。就可以配置多入口。用的不多,因为在开发中通常是单入口

entry:{
   index:"./src/index.js",
   router:"./src/js/router.js",
   vuex:"./src/js/vuex.js"
  },
 output:{	//生成对应文件名的文件
  filename:"js/[name].[contenthash:10].js",
  path:resolve(__dirname,"dist")
}, 

分割第三方模块

以jquery为例:安装jquery第三方:npm i jquery
默认也是把jq打包到了main.js中,我们想分割出jq。

//webpack.config.js
optimization:{
        splitChunks:{
            chunks:'all'
        }
    }

分割自定义模块

需要我们写代码进行分割,在入口JS中写,如下:

import(/* webpackChunkName:'router' */'./js/router').then(res=>{
    console.log(res)
})
import(/* webpackChunkName:'vuex' */'./js/vuex').then(res=>{
    console.log(res)
})

模块的懒加载

所谓的懒加载就是指使用时候再去加载。

//加注释生成的文件名 router
document.getElementById("box1").onclick = function () {
    import(/* webpackChunkName:'router' */"./js/router").then(res=>{
        console.log(res.default())
    })
}	//点击之后再加载需要的数据。

模块的预加载:

所谓的预加载就是提前加载好模块,等待使用。
预加载比懒加载多了一个注释 webpackPrefetch:true

// 预加载  webpackPrefetch:true
document.getElementById("box2").onclick = function () {
    import(/* webpackChunkName:'vuex', webpackPrefetch:true */"./js/vuex").then(res=>{
        console.log(res.default())
    })
}	//还没有点击就加载好的需要的的数据。

PWA

Progressive Web Application 渐进式web应用
通过pwa可以让webapp在没有网络的情况下,看能看到一些数据。

第一步:安装:

npm i workbox-webpack-plugin

第二步:配置

let WorkboxWebpackPlugin = require("workbox-webpack-plugin")

new WorkboxWebpackPlugin.GenerateSW({
     clientsClaim: true, // 快速启动serverWorker
     skipWaiting: true
 })

第三步:在入口文件index.js中注册

if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js').then(registration => {
        console.log('SW registered: ', registration);
        }).catch(registrationError => {
        console.log('SW registration failed: ', registrationError);
        });
    });
}

第三步:安装一个叫serve的模块 这个模块就可以快速的搭建一台服务器

npm i serve -g    如果全局安装了,就可以使用serve命令

第四步:运行 serve -s dist 输入一个地址:http://localhost:5000

配置排除打包

如果项目中有些模块,我们不想进行打包,可以使用排除打包。

//webpack.config.js
   externals:{
       jquery:"jQuery"
       ....
   }

配置resolve:

//webpack.config.js
resolve:{
    // 配置路由的别名  @  src     $myJs   src/js
     // 优点:路径简写了  缺点:没有提示  鸡肋   @xx/a/b/c
     alias:{
         $myJs:resolve(__dirname,"../../src/js")
     },

     // 配置模块匹配的后缀名  Home
     // 鸡肋
     extensions:[".js",".json",".css",".less"],

     // 配置找node_module的目录
     // modules:[resolve(__dirname,"../../node_modules")]
 }

配置代理服务器-跨域

devServer: {
   // 1,最基础的配置
    contentBase: resolve(__dirname, "dist"),
    port: 8080,
    open: true,
    compress: true,
    hot: true, // 支持热模块替换

    // 2,常用配置
    watchContentBase:true, // 监听contentBase下面所有的文件,就重新的reload
    watchOptions:{
        // 忽略监听的模块
        ignored:/node_modules/
    },

    // 3,跨域开发环境的跨域
    proxy:{   //axios:  /api/news 访问当前开发服务器   就会转发到另一个服务器
        //  /news  获取新闻列表    baseURL:http://www.baidu.com
        //  http://www.baidu.com/api/news
        //  http://www.baidu.com/news
        "/api":{
            target:"http://www.baidu.com",  // 后台接口地址
            pathRewrite:{
                "^/api":""
            }
        }
    }

    // 在vue脚本架中  新建一个vue.config.js
    // 在vue脚手架中,隐藏了所有的webpack配置,如果你想,你需要在vue.config.js中配置
    // 你在vue.config.js中配置完毕后,脚手架会帮你合并到webpack中
},

主要理解

 proxy:{  	
   "/api":{	//表示接口地址,固定api
       target:"http://www.baidu.com", 
       pathRewrite:{
           "^/api":""	//以api开头的接口地址 api 换为空
       }
   }
}

到axios请求 /api/news 时,会访问当前开发服务器8080,配置了代理,8080会帮我们转发到http://www.baidu.com ,访问地址就成了http://www.baidu.com/api/news ,但要访问的地址是 /new ,所以需要pathRewrite 将 /api 替换为空,最后就访问了http://www.baidu.com/news

代码

// 一个比较完整版本的用于生产环境的webpack配置
let { resolve } = require("path")
let HtmlWebpackPlugin = require("html-webpack-plugin")
let { CleanWebpackPlugin } = require("clean-webpack-plugin")
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
let MiniCssExtractPlugin = require("mini-css-extract-plugin")
let WorkboxWebpackPlugin = require("workbox-webpack-plugin")

// 处理样式的公共代码
let commonCssLoader = [
    {
        loader: MiniCssExtractPlugin.loader,
        options: {
            publicPath:"../"
        }
    },
    "css-loader",
    {
        loader:"postcss-loader",
        options:{
            ident:"postcss",
            plugins: () => [
                require("postcss-preset-env")()
            ]
        }
    }
]
module.exports = {
    mode:"development",
    entry:"./src/index.js",
    output:{
        // filename:"js/main.[hash:10].js",
        filename:"js/[name].[contenthash:10].js",
        path:resolve(__dirname,"dist")
    },
    module:{
        rules:[
            /*{
                test:/\.js$/,
                exclude:/node_modules/,
                loader:"eslint-loader",
                enforce:"pre",
                options:{
                    fix:true
                }
            },*/
            {
                oneOf:[
                    // 1,样式相关的  css  less  scss  stylus
                    {
                        test:/\.css$/,
                        use:[...commonCssLoader],
                        sideEffects: false
                    },
                    {
                        test:/\.less$/,
                        use:[...commonCssLoader,"less-loader"]
                    },
                    {
                        test:/\.js$/,
                        exclude: /node_modules/,
                        use:[
                         /*   {
                                loader:"thread-loader",
                                options: {
                                    workers:2  // 开启两个进程进行打包
                                }
                            },*/
                            {
                                loader:"babel-loader",
                                options: {
                                    presets:[
                                        [
                                            '@babel/preset-env',
                                            // 配置polyfill的按需加载
                                            {
                                                // 配置按需引入
                                                useBuiltIns:'usage',
                                                corejs:{
                                                    version:3
                                                },
                                                // 指定兼容做到哪个版本的浏览器
                                                targets:{
                                                    chrome:'60',
                                                    firefox:'60',
                                                    ie:'9',
                                                    safari:'10',
                                                    edge:'17'
                                                }
                                            }
                                        ]
                                    ],
                                    // 开启babel缓存
                                    cacheDirectory:true
                                }
                            }
                        ]
                    },
                    // 3,图片相关的
                    {   // 配置是css中的背景图
                        test:/\.(jpg|png|gif)$/,
                        loader:"url-loader",
                        options: {
                            limit:5 * 1024, // 图片小于5kb  会打包成base64
                            name:'[hash:12].[ext]',
                            outputPath:"img"
                        }
                    },
                    {
                        // 配置html模块中的插入图
                        test:/\.html$/,
                        loader:"html-loader",
                    },
                    // 4,字体图标相关的
                    {
                        exclude: /\.(js|json|css|less|jpg|png|gif|html)$/,
                        loader: "file-loader",
                        options: {
                            name:'[hash:12].[ext]',
                            outputPath:"source"
                        }
                    }
                ]
            },
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:"./public/index.html",
            // 配置生成的html文件
            minify:{
                collapseWhitespace:true,
                removeComments:true
                // ....
            }
        }),
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            filename:"css/index.[contenthash:10].css"
        }),
        new OptimizeCssAssetsPlugin(),
        new WorkboxWebpackPlugin.GenerateSW({
            clientsClaim: true, // 快速启动serverWorker
            skipWaiting: true
        })
    ],
    devServer: {
        // 1,最基础的配置
        contentBase: resolve(__dirname, "dist"),
        port: 8080,
        open: true,
        compress: true,
        hot: true, // 支持热模块替换

        // 2,常用配置
        watchContentBase:true, // 监听contentBase下面所有的文件,就重新的reload
        watchOptions:{
            // 忽略监听的模块
            ignored:/node_modules/
        },

        // 3,跨域开发环境的跨域
        proxy:{   //     axios:  /api/news  访问是开发访问   就会转发到另一个服务器
            //  /news  获取新闻列表    baseURL:http://www.baidu.com
            //  http://www.baidu.com/api/news
            //  http://www.baidu.com/news
            "/api":{
                target:"http://www.baidu.com",  // 后台接口地址
                pathRewrite:{
                    "^/api":""
                }
            }
        }

        // 在vue脚本架中  新建一个vue.config.js
        // 在vue脚手架中,隐藏了所有的webpack配置,如果你想,你需要在vue.config.js中配置
        // 你在vue.config.js中配置完毕后,脚手架会帮你合并到webpack中
    },
    // devtool:"source-map",
    optimization:{
        splitChunks:{
            chunks:'all'
        }
    },
    // 配置排除打包
    externals:{
        jquery:"jQuery"
    },
    resolve:{
        // 配置路由的别名  @  src     $myJs   src/js
        // 优点:路径简写了  缺点:没有提示  鸡肋   @xx/a/b/c
        alias:{
            $myJs:resolve(__dirname,"../../src/js")
        },

        // 配置模块匹配的后缀名  Home
        // 鸡肋
        extensions:[".js",".json",".css",".less"],

        // 配置找node_module的目录
        // modules:[resolve(__dirname,"../../node_modules")]
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值